diff --git a/.gitignore b/.gitignore index ebf6ea89..0f0e2fe8 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ aptos/tests .pytest_cache/ +venv/ diff --git a/ethereum/brownie-config.yaml b/ethereum/brownie-config.yaml index 20edd6a6..671be2e9 100644 --- a/ethereum/brownie-config.yaml +++ b/ethereum/brownie-config.yaml @@ -2,141 +2,42 @@ compiler: solc: remappings: - '@openzeppelin=OpenZeppelin/openzeppelin-contracts@4.6.0' + # on bevm + version: 0.8.13 dependencies: - OpenZeppelin/openzeppelin-contracts@4.6.0 dotenv: .env networks: - optimism-main: - dola_chain_id: 24 - core_emitter: '0xabbce6c0c2c7cd213f4c69f8a685f6dfc1848b6e3f31dd15872f4e777d5b3e86' - pools: - ETH: - dola_chain_id: 24 - dola_pool_id: 4 - pool_address: '0x0000000000000000000000000000000000000000' - pool_name: ETH - pool_weight: 1 - USDC: - dola_chain_id: 24 - dola_pool_id: 2 - pool_address: '0x7F5c764cBc14f9669B88837ca1490cCa17c31607' - pool_name: USDC - pool_weight: 1 - USDT: - dola_chain_id: 24 - dola_pool_id: 1 - pool_address: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58' - pool_name: USDT - pool_weight: 1 - WBTC: - dola_chain_id: 24 - dola_pool_id: 0 - pool_address: '0x68f180fcCe6836688e9084f035309E29Bf0A2095' - pool_name: WBTC - pool_weight: 1 - OP: - dola_chain_id: 24 - dola_pool_id: 7 - pool_address: '0x4200000000000000000000000000000000000042' - pool_name: OP - pool_weight: 1 - scan_rpc_url: https://api-optimistic.etherscan.io/api - graphql_url: https://api.studio.thegraph.com/query/48530/opdolarelayer/v0.0.3 - endpoints: [ "https://rpc.ankr.com/optimism", "https://optimism.api.onfinality.io/public", "https://mainnet.optimism.io", "https://1rpc.io/op", "https://api.zan.top/node/v1/opt/mainnet/public", "https://optimism.blockpi.network/v1/rpc/public" ] - tokens: - ETH: - address: '0x0000000000000000000000000000000000000000' - decimals: 18 - dola_pool_id: 4 - OP: - address: '0x4200000000000000000000000000000000000042' - decimals: 18 - dola_pool_id: 7 - USDC: - address: '0x7F5c764cBc14f9669B88837ca1490cCa17c31607' - decimals: 6 - dola_pool_id: 2 - USDT: - address: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58' - decimals: 6 - dola_pool_id: 1 - WBTC: - address: '0x68f180fcCe6836688e9084f035309E29Bf0A2095' - decimals: 8 - dola_pool_id: 0 - wormhole: '0xEe91C335eab126dF5fDB3797EA9d6aD93aeC9722' - wormhole_chainid: 24 - wormhole_adapter_pool: - v2: "0x94650D61b940496b1BD88767b7B541b1121e0cCF" - v3: "0xD4f0968c718E2b3F6fC2C9da3341c5a0C4720d68" - latest: '0xD4f0968c718E2b3F6fC2C9da3341c5a0C4720d68' - lending_portal: '0xB4011A52C1F49A8B51161485D39BA1e70906bE38' - system_portal: '0x8F3Ff39FE8565b787453F3e5735BF6f0863853aC' - dola_pool: '0x233DDEce6a96c49ecE6Ad9ae820690fE62a28975' - not_involve_fund_consistency: 200 - involve_fund_consistency: 200 - base-main: - dola_chain_id: 30 - core_emitter: '0xabbce6c0c2c7cd213f4c69f8a685f6dfc1848b6e3f31dd15872f4e777d5b3e86' - pools: - ETH: - dola_chain_id: 30 - dola_pool_id: 4 - pool_address: '0x0000000000000000000000000000000000000000' - pool_name: ETH - pool_weight: 1 - USDC: - dola_chain_id: 30 - dola_pool_id: 2 - pool_address: '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA' - pool_name: USDC - pool_weight: 1 - scan_rpc_url: https://api.basescan.org/api - graphql_url: https://api.studio.thegraph.com/query/48530/opdolarelayer/version/latest - endpoints: [ "https://1rpc.io/base", "https://base.blockpi.network/v1/rpc/public", "https://base.meowrpc.com", "https://base-mainnet.public.blastapi.io", "https://mainnet.base.org" ] - tokens: - ETH: - address: '0x0000000000000000000000000000000000000000' - decimals: 18 - dola_pool_id: 4 - USDC: - address: '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA' - decimals: 6 - dola_pool_id: 2 - wormhole: '0xbebdb6C8ddC678FfA9f8748f85C815C556Dd8ac6' - wormhole_chainid: 30 - wormhole_adapter_pool: - v3: "0x0F4aedfB8DA8aF176DefF282DA86EBbe3A0EA19e" - latest: '0x0F4aedfB8DA8aF176DefF282DA86EBbe3A0EA19e' - lending_portal: '0x92113298552f99271cb0b9545C40096eE7B0a351' - system_portal: '0x845fB08635f3C19944ed2750787D262d92607d8C' - dola_pool: '0x68953027738216A63B39D55B18C02FeD5c329Dfa' - not_involve_fund_consistency: 200 - involve_fund_consistency: 200 arbitrum-main: core_emitter: '0xabbce6c0c2c7cd213f4c69f8a685f6dfc1848b6e3f31dd15872f4e777d5b3e86' dola_chain_id: 23 - lending_portal: '0x01ebb222C04894Fc402F8772d2BF2b19D2eEA607' - system_portal: '0x9AB575ab5b2c58c07345cE2DB9b36F0CF54753A5' - wormhole_adapter_pool: - v1: "0x37c15055bf8146E2BF904DACF71b53cc3779Bf49" - v2: "0x098D26E4d2E98C1Dde14C543Eb6804Fd98Af9CB4" - v3: "0x4d6CAB4f234736B9E149E709CE6f45CE04a11cE5" - latest: '0x4d6CAB4f234736B9E149E709CE6f45CE04a11cE5' dola_pool: '0x53eCC006a0073b3351E9e38d94f052E3864C7935' + endpoints: + - https://1rpc.io/arb + - https://endpoints.omniatech.io/v1/arbitrum/one/public + - https://arb-mainnet-public.unifra.io + - https://arb1.arbitrum.io/rpc + - https://rpc.ankr.com/arbitrum + - https://arbitrum.blockpi.network/v1/rpc/public + - https://arbitrum-one.public.blastapi.io + - https://arbitrum.meowrpc.com + graphql_url: https://api.studio.thegraph.com/query/48530/arbdolarelayer/v0.0.3 + involve_fund_consistency: 200 + lending_portal: '0x01ebb222C04894Fc402F8772d2BF2b19D2eEA607' + not_involve_fund_consistency: 200 pools: - ETH: - dola_chain_id: 23 - dola_pool_id: 4 - pool_address: '0x0000000000000000000000000000000000000000' - pool_name: ETH - pool_weight: 1 ARB: dola_chain_id: 23 dola_pool_id: 6 pool_address: '0x912CE59144191C1204E64559FE8253a0e49E6548' pool_name: ARB pool_weight: 1 + ETH: + dola_chain_id: 23 + dola_pool_id: 4 + pool_address: '0x0000000000000000000000000000000000000000' + pool_name: ETH + pool_weight: 1 USDC: dola_chain_id: 23 dola_pool_id: 2 @@ -155,18 +56,17 @@ networks: pool_address: '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f' pool_name: WBTC pool_weight: 1 - endpoints: [ "https://1rpc.io/arb", "https://endpoints.omniatech.io/v1/arbitrum/one/public", "https://arb-mainnet-public.unifra.io", "https://arb1.arbitrum.io/rpc", "https://rpc.ankr.com/arbitrum", "https://arbitrum.blockpi.network/v1/rpc/public", "https://arbitrum-one.public.blastapi.io", "https://arbitrum.meowrpc.com" ] scan_rpc_url: https://api.arbiscan.io/api - graphql_url: https://api.studio.thegraph.com/query/48530/arbdolarelayer/v0.0.3 + system_portal: '0x9AB575ab5b2c58c07345cE2DB9b36F0CF54753A5' tokens: - ETH: - address: '0x0000000000000000000000000000000000000000' - decimals: 18 - dola_pool_id: 4 ARB: address: '0x912CE59144191C1204E64559FE8253a0e49E6548' decimals: 18 dola_pool_id: 6 + ETH: + address: '0x0000000000000000000000000000000000000000' + decimals: 18 + dola_pool_id: 4 USDC: address: '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8' decimals: 6 @@ -180,9 +80,12 @@ networks: decimals: 8 dola_pool_id: 0 wormhole: '0xa5f208e072434bC67592E4C49C1B991BA79BCA46' + wormhole_adapter_pool: + latest: '0x4d6CAB4f234736B9E149E709CE6f45CE04a11cE5' + v1: '0x37c15055bf8146E2BF904DACF71b53cc3779Bf49' + v2: '0x098D26E4d2E98C1Dde14C543Eb6804Fd98Af9CB4' + v3: '0x4d6CAB4f234736B9E149E709CE6f45CE04a11cE5' wormhole_chainid: 23 - not_involve_fund_consistency: 200 - involve_fund_consistency: 200 arbitrum-test: core_emitter: '0xd978abe951a42fb60f00015718efa1e1a9f2256676419fb66551db2816d983b7' scan_rpc_url: https://api-goerli.arbiscan.io/api @@ -190,14 +93,12 @@ networks: wormhole_chainid: 23 wormhole_finality: 200 avax-test: - wormhole: '0x7bbcE28e64B3F8b84d876Ab298393c38ad7aac4C' - wormhole_chainid: 6 core_emitter: '0x4f9f241cd3a249e0ef3d9ece8b1cd464c38c95d6d65c11a2ddd5645632e6e8a0' - lending_portal: '0x18928c95bA9f91a3ad814a7C2DFdCF03f1725a36' - system_portal: '0x7628C9886Dba014c6DB310fa9f32CA520174473B' - wormhole_adapter_pool: '0xF3d8cFbEee2A16c47b8f5f05f6452Bf38b0346Ec' dola_pool: '0xcADD60D3DCF8146c57Df0F8b672696F84E1EEc8e' graphql_url: https://api.studio.thegraph.com/query/48530/fujidola/version/latest + involve_fund_consistency: 200 + lending_portal: '0x18928c95bA9f91a3ad814a7C2DFdCF03f1725a36' + not_involve_fund_consistency: 200 pools: USDC: dola_chain_id: 6 @@ -217,6 +118,7 @@ networks: pool_address: '0x351B7351F2DE7Cb58b37CEDd4c61A7B804F58170' pool_name: WBTC pool_weight: 1 + system_portal: '0x7628C9886Dba014c6DB310fa9f32CA520174473B' tokens: USDC: address: '0x92682ce0591b788767dCF2997df74Ac50C0b50CD' @@ -230,8 +132,67 @@ networks: address: '0x351B7351F2DE7Cb58b37CEDd4c61A7B804F58170' decimals: 18 dola_pool_id: 0 - not_involve_fund_consistency: 200 + wormhole: '0x7bbcE28e64B3F8b84d876Ab298393c38ad7aac4C' + wormhole_adapter_pool: '0xF3d8cFbEee2A16c47b8f5f05f6452Bf38b0346Ec' + wormhole_chainid: 6 + base-main: + core_emitter: '0xabbce6c0c2c7cd213f4c69f8a685f6dfc1848b6e3f31dd15872f4e777d5b3e86' + dola_chain_id: 30 + dola_pool: '0x68953027738216A63B39D55B18C02FeD5c329Dfa' + endpoints: + - https://1rpc.io/base + - https://base.blockpi.network/v1/rpc/public + - https://base.meowrpc.com + - https://base-mainnet.public.blastapi.io + - https://mainnet.base.org + graphql_url: https://api.studio.thegraph.com/query/48530/opdolarelayer/version/latest involve_fund_consistency: 200 + lending_portal: '0x92113298552f99271cb0b9545C40096eE7B0a351' + not_involve_fund_consistency: 200 + pools: + ETH: + dola_chain_id: 30 + dola_pool_id: 4 + pool_address: '0x0000000000000000000000000000000000000000' + pool_name: ETH + pool_weight: 1 + USDC: + dola_chain_id: 30 + dola_pool_id: 2 + pool_address: '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA' + pool_name: USDC + pool_weight: 1 + scan_rpc_url: https://api.basescan.org/api + system_portal: '0x845fB08635f3C19944ed2750787D262d92607d8C' + tokens: + ETH: + address: '0x0000000000000000000000000000000000000000' + decimals: 18 + dola_pool_id: 4 + USDC: + address: '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA' + decimals: 6 + dola_pool_id: 2 + wormhole: '0xbebdb6C8ddC678FfA9f8748f85C815C556Dd8ac6' + wormhole_adapter_pool: + latest: '0x0F4aedfB8DA8aF176DefF282DA86EBbe3A0EA19e' + v3: '0x0F4aedfB8DA8aF176DefF282DA86EBbe3A0EA19e' + wormhole_chainid: 30 + bevm-test: + bool_adapter_pool: + latest: '0xb62bb643DB4e67AD2c1D90cC6Fb974008B0cbE79' + dola_pool: '0x08787609d217C26067c616e105C6C410bA6117B6' + dst_bool_anchor: '0x7ef294a55be19df7f287653a876845abb1e75badf8e0c9a1a310ffbfd2518ee6' + dst_bool_chainid: 1918346523 + endpoints: + - https://test-rpc-node-http.bool.network + lending_portal_bool: '0x3c350686283d411d0A7C5e270cB68Bc223650172' + src_bool_anchor: '0x45395fc32da85a18285639f881cc1b7785c91091' + src_dola_chainid: 1502 + system_portal_bool: '0x9F9954616e32c5C531d4B53D07F1Cc4965e59698' + usdc: '0x59a536E000E9F0825eC7937DfcfD80105e43678e' + usdt: '0xfc016a12EA26eb2fa0C85Bb19a842953C6510C49' + wbtc: '0x669626171F603B7C2d7B909C1A5275DbDa8e319D' bsc-test: btc: '0x534BF426a64d93d4CaCeCFa1Eb1FD1aCC2A988a5' lending_portal: '0x3bDCd3dE2FAaEed58Ae5E9FD5e7c92Bfa938B5d0' @@ -251,18 +212,101 @@ networks: goerli: wormhole: '0x706abc4E45D419950511e474C7B9Ed348A4a716c' wormhole_chainid: 2 + optimism-main: + core_emitter: '0xabbce6c0c2c7cd213f4c69f8a685f6dfc1848b6e3f31dd15872f4e777d5b3e86' + dola_chain_id: 24 + dola_pool: '0x233DDEce6a96c49ecE6Ad9ae820690fE62a28975' + endpoints: + - https://rpc.ankr.com/optimism + - https://optimism.api.onfinality.io/public + - https://mainnet.optimism.io + - https://1rpc.io/op + - https://api.zan.top/node/v1/opt/mainnet/public + - https://optimism.blockpi.network/v1/rpc/public + graphql_url: https://api.studio.thegraph.com/query/48530/opdolarelayer/v0.0.3 + involve_fund_consistency: 200 + lending_portal: '0xB4011A52C1F49A8B51161485D39BA1e70906bE38' + not_involve_fund_consistency: 200 + pools: + ETH: + dola_chain_id: 24 + dola_pool_id: 4 + pool_address: '0x0000000000000000000000000000000000000000' + pool_name: ETH + pool_weight: 1 + OP: + dola_chain_id: 24 + dola_pool_id: 7 + pool_address: '0x4200000000000000000000000000000000000042' + pool_name: OP + pool_weight: 1 + USDC: + dola_chain_id: 24 + dola_pool_id: 2 + pool_address: '0x7F5c764cBc14f9669B88837ca1490cCa17c31607' + pool_name: USDC + pool_weight: 1 + USDT: + dola_chain_id: 24 + dola_pool_id: 1 + pool_address: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58' + pool_name: USDT + pool_weight: 1 + WBTC: + dola_chain_id: 24 + dola_pool_id: 0 + pool_address: '0x68f180fcCe6836688e9084f035309E29Bf0A2095' + pool_name: WBTC + pool_weight: 1 + scan_rpc_url: https://api-optimistic.etherscan.io/api + system_portal: '0x8F3Ff39FE8565b787453F3e5735BF6f0863853aC' + tokens: + ETH: + address: '0x0000000000000000000000000000000000000000' + decimals: 18 + dola_pool_id: 4 + OP: + address: '0x4200000000000000000000000000000000000042' + decimals: 18 + dola_pool_id: 7 + USDC: + address: '0x7F5c764cBc14f9669B88837ca1490cCa17c31607' + decimals: 6 + dola_pool_id: 2 + USDT: + address: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58' + decimals: 6 + dola_pool_id: 1 + WBTC: + address: '0x68f180fcCe6836688e9084f035309E29Bf0A2095' + decimals: 8 + dola_pool_id: 0 + wormhole: '0xEe91C335eab126dF5fDB3797EA9d6aD93aeC9722' + wormhole_adapter_pool: + latest: '0xD4f0968c718E2b3F6fC2C9da3341c5a0C4720d68' + v2: '0x94650D61b940496b1BD88767b7B541b1121e0cCF' + v3: '0xD4f0968c718E2b3F6fC2C9da3341c5a0C4720d68' + wormhole_chainid: 24 polygon-main: core_emitter: '0xabbce6c0c2c7cd213f4c69f8a685f6dfc1848b6e3f31dd15872f4e777d5b3e86' dola_chain_id: 5 - lending_portal: '0xD51Ef1Cf286cb403ec2767e7b52Ce402f37E930d' - system_portal: '0x75950Df2bAfa1b0EE5dB22aaa5e00EBCED982315' - wormhole_adapter_pool: - v1: "0x6A028B4911078F80A20c8De434316C427E3A6Fa5" - v2: "0x4445c48e9B70F78506E886880a9e09B501ED1E13" - v3: "0x8F65495ca94cCdb3F159369Cf27a91464Db87E98" - v4: "0xb4da6261C07330C6Cb216159dc38fa3B302BC8B5" - latest: '0xb4da6261C07330C6Cb216159dc38fa3B302BC8B5' dola_pool: '0xC3Eb696184b8927D677D8AB390A26563De4798c3' + endpoints: + - https://polygon.llamarpc.com + - https://rpc-mainnet.maticvigil.com + - https://endpoints.omniatech.io/v1/matic/mainnet/public + - https://polygon-rpc.com + - https://rpc-mainnet.matic.quiknode.pro + - https://poly-rpc.gateway.pokt.network + - https://rpc.ankr.com/polygon + - https://polygon-mainnet.public.blastapi.io + - https://1rpc.io/matic + - https://polygon.meowrpc.com + - https://polygon-bor.publicnode.com + graphql_url: https://api.studio.thegraph.com/query/48530/polygondolarelayer/v0.0.4 + involve_fund_consistency: 1 + lending_portal: '0xD51Ef1Cf286cb403ec2767e7b52Ce402f37E930d' + not_involve_fund_consistency: 1 pools: MATIC: dola_chain_id: 5 @@ -289,8 +333,7 @@ networks: pool_name: WBTC pool_weight: 1 scan_rpc_url: https://api.polygonscan.com/api - graphql_url: https://api.studio.thegraph.com/query/48530/polygondolarelayer/v0.0.4 - endpoints: [ "https://polygon.llamarpc.com", "https://rpc-mainnet.maticvigil.com", "https://endpoints.omniatech.io/v1/matic/mainnet/public", "https://polygon-rpc.com", "https://rpc-mainnet.matic.quiknode.pro", "https://poly-rpc.gateway.pokt.network", "https://rpc.ankr.com/polygon", "https://polygon-mainnet.public.blastapi.io", "https://1rpc.io/matic", "https://polygon.meowrpc.com", "https://polygon-bor.publicnode.com" ] + system_portal: '0x75950Df2bAfa1b0EE5dB22aaa5e00EBCED982315' tokens: MATIC: address: '0x0000000000000000000000000000000000000000' @@ -309,15 +352,19 @@ networks: decimals: 8 dola_pool_id: 0 wormhole: '0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7' + wormhole_adapter_pool: + latest: '0xb4da6261C07330C6Cb216159dc38fa3B302BC8B5' + v1: '0x6A028B4911078F80A20c8De434316C427E3A6Fa5' + v2: '0x4445c48e9B70F78506E886880a9e09B501ED1E13' + v3: '0x8F65495ca94cCdb3F159369Cf27a91464Db87E98' + v4: '0xb4da6261C07330C6Cb216159dc38fa3B302BC8B5' wormhole_chainid: 5 - not_involve_fund_consistency: 1 - involve_fund_consistency: 1 polygon-test: core_emitter: '0x4f9f241cd3a249e0ef3d9ece8b1cd464c38c95d6d65c11a2ddd5645632e6e8a0' - lending_portal: '0x1aDA087B6c051C77003966d669D1149d0c2dEE5F' - scan_rpc_url: https://api-testnet.polygonscan.com/api - system_portal: '0x2c0E6c7F14014F9D5D89F12DaB5c2Ea6eEfc481b' graphql_url: https://api.studio.thegraph.com/query/48530/mumbaidola/version/latest + involve_fund_consistency: 200 + lending_portal: '0x1aDA087B6c051C77003966d669D1149d0c2dEE5F' + not_involve_fund_consistency: 200 pools: MATIC: dola_chain_id: 5 @@ -343,6 +390,8 @@ networks: pool_address: '0xE0332679Baf5d55Da6dbcF8199322F111c7005D8' pool_name: WBTC pool_weight: 1 + scan_rpc_url: https://api-testnet.polygonscan.com/api + system_portal: '0x2c0E6c7F14014F9D5D89F12DaB5c2Ea6eEfc481b' tokens: MATIC: address: '0x0000000000000000000000000000000000000000' @@ -363,8 +412,6 @@ networks: wormhole: '0x0CBE91CF822c73C2315FB05100C2F714765d5c20' wormhole_adapter_pool: '0x83B787B99B1f5E9D90eDcf7C09E41A5b336939A7' wormhole_chainid: 5 - not_involve_fund_consistency: 200 - involve_fund_consistency: 200 polygon-zk-test: btc: '0x9895bcd049eE00560599b1458fC2436793E3E011' lending_portal: '0x4e7AE7De13Db1587EA9B6C306299f66c3991D57e' diff --git a/ethereum/contracts/dolaportal/LendingBool.sol b/ethereum/contracts/dolaportal/LendingBool.sol new file mode 100644 index 00000000..d8436f78 --- /dev/null +++ b/ethereum/contracts/dolaportal/LendingBool.sol @@ -0,0 +1,416 @@ +// Copyright (c) OmniBTC, Inc. +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "../../interfaces/IBoolAdapterPool.sol"; +import "../libraries/LibBoolAdapterVerify.sol"; +import "../libraries/LibLendingCodec.sol"; +import "../libraries/LibDecimals.sol"; +import "../libraries/LibDolaTypes.sol"; +import "../libraries/LibAsset.sol"; + +contract LendingPortalBool { + uint8 public constant LENDING_APP_ID = 1; + + IBoolAdapterPool public immutable boolAdapterPool; + + event RelayEvent( + uint64 sequence, + uint64 nonce, + uint256 feeAmount, + uint16 appId, + uint8 callType + ); + + event LendingPortalEvent( + uint64 nonce, + address sender, + bytes dolaPoolAddress, + uint16 sourceChainId, + uint16 dstChainId, + bytes receiver, + uint64 amount, + uint8 callType + ); + + constructor(IBoolAdapterPool _boolAdapterPool) { + boolAdapterPool = _boolAdapterPool; + } + + function supply( + address token, + uint256 amount, + uint256 fee + ) external payable { + uint64 nonce = IBoolAdapterPool(boolAdapterPool).getNonce(); + uint64 fixAmount = LibDecimals.fixAmountDecimals( + amount, + LibAsset.queryDecimals(token) + ); + uint16 dolaChainId = boolAdapterPool.dolaChainId(); + bytes memory appPayload = LibLendingCodec.encodeDepositPayload( + dolaChainId, + nonce, + LibDolaTypes.addressToDolaAddress(dolaChainId, msg.sender), + LibLendingCodec.SUPPLY + ); + // Deposit assets to the pool and perform amount checks + LibAsset.depositAsset(token, amount); + if (!LibAsset.isNativeAsset(token)) { + LibAsset.maxApproveERC20( + IERC20(token), + address(boolAdapterPool), + amount + ); + } + + appPayload = LibBoolAdapterVerify.remapping_opcode( + appPayload, + LibBoolAdapterVerify.SERVER_OPCODE_LENDING_SUPPLY + ); + + IBoolAdapterPool(boolAdapterPool).sendDeposit{ + value: msg.value - fee + }(token, amount, LENDING_APP_ID, appPayload); + + address relayer = IBoolAdapterPool(boolAdapterPool) + .getOneRelayer(nonce); + + LibAsset.transferAsset(address(0), payable(relayer), fee); + + emit RelayEvent( + 0, + nonce, + fee, + LENDING_APP_ID, + LibLendingCodec.SUPPLY + ); + + emit LendingPortalEvent( + nonce, + msg.sender, + abi.encodePacked(token), + dolaChainId, + 0, + abi.encodePacked(msg.sender), + fixAmount, + LibLendingCodec.SUPPLY + ); + } + + // withdraw use 8 decimal + function withdraw( + bytes memory token, + bytes memory receiver, + uint16 dstChainId, + uint64 amount, + uint256 fee + ) external payable { + uint64 nonce = IBoolAdapterPool(boolAdapterPool).getNonce(); + uint16 dolaChainId = boolAdapterPool.dolaChainId(); + + bytes memory appPayload = LibLendingCodec.encodeWithdrawPayload( + dolaChainId, + nonce, + amount, + LibDolaTypes.DolaAddress(dolaChainId, token), + LibDolaTypes.DolaAddress(dstChainId, receiver), + LibLendingCodec.WITHDRAW + ); + + appPayload = LibBoolAdapterVerify.remapping_opcode( + appPayload, + LibBoolAdapterVerify.SERVER_OPCODE_LENDING_WITHDRAW + ); + + IBoolAdapterPool(boolAdapterPool).sendMessage{ + value: msg.value - fee + }(LENDING_APP_ID, appPayload); + + address relayer = IBoolAdapterPool(boolAdapterPool) + .getOneRelayer(nonce); + + LibAsset.transferAsset(address(0), payable(relayer), fee); + + emit RelayEvent( + 0, + nonce, + fee, + LENDING_APP_ID, + LibLendingCodec.WITHDRAW + ); + + emit LendingPortalEvent( + nonce, + msg.sender, + abi.encodePacked(token), + dolaChainId, + dstChainId, + receiver, + amount, + LibLendingCodec.WITHDRAW + ); + } + + function borrow( + bytes memory token, + bytes memory receiver, + uint16 dstChainId, + uint64 amount, + uint256 fee + ) external payable { + uint64 nonce = IBoolAdapterPool(boolAdapterPool).getNonce(); + uint16 dolaChainId = boolAdapterPool.dolaChainId(); + + bytes memory appPayload = LibLendingCodec.encodeWithdrawPayload( + dolaChainId, + nonce, + amount, + LibDolaTypes.DolaAddress(dolaChainId, token), + LibDolaTypes.DolaAddress(dstChainId, receiver), + LibLendingCodec.BORROW + ); + + appPayload = LibBoolAdapterVerify.remapping_opcode( + appPayload, + LibBoolAdapterVerify.SERVER_OPCODE_LENDING_BORROW + ); + + IBoolAdapterPool(boolAdapterPool).sendMessage{ + value: msg.value - fee + }(LENDING_APP_ID, appPayload); + + address relayer = IBoolAdapterPool(boolAdapterPool) + .getOneRelayer(nonce); + + LibAsset.transferAsset(address(0), payable(relayer), fee); + + emit RelayEvent( + 0, + nonce, + fee, + LENDING_APP_ID, + LibLendingCodec.BORROW + ); + + emit LendingPortalEvent( + nonce, + msg.sender, + abi.encodePacked(token), + dolaChainId, + dstChainId, + receiver, + amount, + LibLendingCodec.BORROW + ); + } + + function repay( + address token, + uint256 amount, + uint256 fee + ) external payable { + uint64 nonce = IBoolAdapterPool(boolAdapterPool).getNonce(); + uint64 fixAmount = LibDecimals.fixAmountDecimals( + amount, + LibAsset.queryDecimals(token) + ); + uint16 dolaChainId = boolAdapterPool.dolaChainId(); + + bytes memory appPayload = LibLendingCodec.encodeDepositPayload( + dolaChainId, + nonce, + LibDolaTypes.addressToDolaAddress(dolaChainId, msg.sender), + LibLendingCodec.REPAY + ); + + // Deposit assets to the pool and perform amount checks + LibAsset.depositAsset(token, amount); + if (!LibAsset.isNativeAsset(token)) { + LibAsset.maxApproveERC20( + IERC20(token), + address(boolAdapterPool), + amount + ); + } + + appPayload = LibBoolAdapterVerify.remapping_opcode( + appPayload, + LibBoolAdapterVerify.SERVER_OPCODE_LENDING_REPAY + ); + + IBoolAdapterPool(boolAdapterPool).sendDeposit{ + value: msg.value - fee + }(token, amount, LENDING_APP_ID, appPayload); + + address relayer = IBoolAdapterPool(boolAdapterPool) + .getOneRelayer(nonce); + + LibAsset.transferAsset(address(0), payable(relayer), fee); + + emit RelayEvent( + 0, + nonce, + fee, + LENDING_APP_ID, + LibLendingCodec.REPAY + ); + + emit LendingPortalEvent( + nonce, + msg.sender, + abi.encodePacked(token), + dolaChainId, + 0, + abi.encodePacked(msg.sender), + fixAmount, + LibLendingCodec.REPAY + ); + } + + function liquidate( + uint16 repayPoolId, + uint64 liquidateUserId, + uint16 liquidatePoolId, + uint256 fee + ) external payable { + uint64 nonce = IBoolAdapterPool(boolAdapterPool).getNonce(); + uint16 dolaChainId = boolAdapterPool.dolaChainId(); + + bytes memory appPayload = LibLendingCodec.encodeLiquidatePayloadV2( + dolaChainId, + nonce, + repayPoolId, + liquidateUserId, + liquidatePoolId + ); + + appPayload = LibBoolAdapterVerify.remapping_opcode( + appPayload, + LibBoolAdapterVerify.SERVER_OPCODE_LENDING_LIQUIDATE + ); + + // Deposit assets to the pool and perform amount checks + + IBoolAdapterPool(boolAdapterPool).sendMessage{ + value: msg.value - fee + }(LENDING_APP_ID, appPayload); + + address relayer = IBoolAdapterPool(boolAdapterPool) + .getOneRelayer(nonce); + + LibAsset.transferAsset(address(0), payable(relayer), fee); + + emit RelayEvent( + 0, + nonce, + fee, + LENDING_APP_ID, + LibLendingCodec.LIQUIDATE + ); + + emit LendingPortalEvent( + nonce, + msg.sender, + abi.encodePacked(msg.sender), + dolaChainId, + 0, + abi.encodePacked(msg.sender), + 0, + LibLendingCodec.LIQUIDATE + ); + } + + function as_collateral(uint16[] memory dolaPoolIds, uint256 fee) + external + payable + { + uint64 nonce = IBoolAdapterPool(boolAdapterPool).getNonce(); + uint16 dolaChainId = boolAdapterPool.dolaChainId(); + + bytes memory appPayload = LibLendingCodec.encodeManageCollateralPayload( + dolaPoolIds, + LibLendingCodec.AS_COLLATERAL + ); + + appPayload = LibBoolAdapterVerify.remapping_opcode( + appPayload, + LibBoolAdapterVerify.SERVER_OPCODE_LENDING_COLLATERAL + ); + + IBoolAdapterPool(boolAdapterPool).sendMessage{ + value: msg.value - fee + }(LENDING_APP_ID, appPayload); + + address relayer = IBoolAdapterPool(boolAdapterPool) + .getOneRelayer(nonce); + + LibAsset.transferAsset(address(0), payable(relayer), fee); + + emit RelayEvent( + 0, + nonce, + fee, + LENDING_APP_ID, + LibLendingCodec.AS_COLLATERAL + ); + + emit LendingPortalEvent( + nonce, + msg.sender, + bytes(""), + dolaChainId, + 0, + abi.encodePacked(msg.sender), + 0, + LibLendingCodec.AS_COLLATERAL + ); + } + + function cancel_as_collateral(uint16[] memory dolaPoolIds, uint256 fee) + external + payable + { + uint64 nonce = IBoolAdapterPool(boolAdapterPool).getNonce(); + uint16 dolaChainId = boolAdapterPool.dolaChainId(); + + bytes memory appPayload = LibLendingCodec.encodeManageCollateralPayload( + dolaPoolIds, + LibLendingCodec.CANCEL_AS_COLLATERAL + ); + + appPayload = LibBoolAdapterVerify.remapping_opcode( + appPayload, + LibBoolAdapterVerify.SERVER_OPCODE_LENDING_CANCEL_COLLATERAL + ); + + IBoolAdapterPool(boolAdapterPool).sendMessage{ + value: msg.value - fee + }(LENDING_APP_ID, appPayload); + + address relayer = IBoolAdapterPool(boolAdapterPool) + .getOneRelayer(nonce); + + LibAsset.transferAsset(address(0), payable(relayer), fee); + + emit RelayEvent( + 0, + nonce, + fee, + LENDING_APP_ID, + LibLendingCodec.CANCEL_AS_COLLATERAL + ); + + emit LendingPortalEvent( + nonce, + msg.sender, + bytes(""), + dolaChainId, + 0, + abi.encodePacked(msg.sender), + 0, + LibLendingCodec.CANCEL_AS_COLLATERAL + ); + } +} diff --git a/ethereum/contracts/dolaportal/SystemBool.sol b/ethereum/contracts/dolaportal/SystemBool.sol new file mode 100644 index 00000000..09f99dfd --- /dev/null +++ b/ethereum/contracts/dolaportal/SystemBool.sol @@ -0,0 +1,132 @@ +// Copyright (c) OmniBTC, Inc. +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "../../interfaces/IBoolAdapterPool.sol"; +import "../libraries/LibBoolAdapterVerify.sol"; +import "../libraries/LibSystemCodec.sol"; +import "../libraries/LibDecimals.sol"; +import "../libraries/LibDolaTypes.sol"; +import "../libraries/LibAsset.sol"; + +contract SystemPortalBool { + uint8 public constant SYSTEM_APP_ID = 0; + + IBoolAdapterPool public immutable boolAdapterPool; + + event RelayEvent( + uint64 sequence, + uint64 nonce, + uint256 feeAmount, + uint16 appId, + uint8 callType + ); + + event SystemPortalEvent( + uint64 nonce, + address sender, + uint16 sourceChainId, + uint16 userChainId, + bytes userAddress, + uint8 callType + ); + + constructor(IBoolAdapterPool _boolAdapterPool) { + boolAdapterPool = _boolAdapterPool; + } + + function binding( + uint16 bindDolaChainId, + bytes memory bindAddress, + uint256 fee + ) external payable { + uint64 nonce = IBoolAdapterPool(boolAdapterPool).getNonce(); + uint16 dolaChainId = boolAdapterPool.dolaChainId(); + + bytes memory appPayload = LibSystemCodec.encodeBindPayload( + bindDolaChainId, + nonce, + LibDolaTypes.DolaAddress(bindDolaChainId, bindAddress), + LibSystemCodec.BINDING + ); + + appPayload = LibBoolAdapterVerify.remapping_opcode( + appPayload, + LibBoolAdapterVerify.SERVER_OPCODE_SYSTEM_BINDING + ); + + IBoolAdapterPool(boolAdapterPool).sendMessage{ + value: msg.value - fee + }(SYSTEM_APP_ID, appPayload); + + address relayer = IBoolAdapterPool(boolAdapterPool) + .getOneRelayer(nonce); + + LibAsset.transferAsset(address(0), payable(relayer), fee); + + emit RelayEvent( + 0, + nonce, + fee, + SYSTEM_APP_ID, + LibSystemCodec.BINDING + ); + + emit SystemPortalEvent( + nonce, + msg.sender, + dolaChainId, + bindDolaChainId, + bindAddress, + LibSystemCodec.BINDING + ); + } + + function unbinding( + uint16 unbindDolaChainId, + bytes memory unbindAddress, + uint256 fee + ) external payable { + uint64 nonce = IBoolAdapterPool(boolAdapterPool).getNonce(); + uint16 dolaChainId = boolAdapterPool.dolaChainId(); + + bytes memory appPayload = LibSystemCodec.encodeBindPayload( + unbindDolaChainId, + nonce, + LibDolaTypes.DolaAddress(unbindDolaChainId, unbindAddress), + LibSystemCodec.UNBINDING + ); + + appPayload = LibBoolAdapterVerify.remapping_opcode( + appPayload, + LibBoolAdapterVerify.SERVER_OPCODE_SYSTEM_UNBINDING + ); + + IBoolAdapterPool(boolAdapterPool).sendMessage{ + value: msg.value - fee + }(SYSTEM_APP_ID, appPayload); + + address relayer = IBoolAdapterPool(boolAdapterPool) + .getOneRelayer(nonce); + + LibAsset.transferAsset(address(0), payable(relayer), fee); + + emit RelayEvent( + 0, + nonce, + fee, + SYSTEM_APP_ID, + LibSystemCodec.UNBINDING + ); + + emit SystemPortalEvent( + nonce, + msg.sender, + dolaChainId, + unbindDolaChainId, + unbindAddress, + LibSystemCodec.UNBINDING + ); + } +} diff --git a/ethereum/contracts/libraries/LibBoolAdapterVerify.sol b/ethereum/contracts/libraries/LibBoolAdapterVerify.sol new file mode 100644 index 00000000..e2fe4863 --- /dev/null +++ b/ethereum/contracts/libraries/LibBoolAdapterVerify.sol @@ -0,0 +1,56 @@ +// Copyright (c) OmniBTC, Inc. +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.0; + +import "./LibBytes.sol"; +import "./LibGovCodec.sol"; +import "./LibPoolCodec.sol"; + +library LibBoolAdapterVerify { + uint8 internal constant OFFSET = 64; + + uint8 internal constant SERVER_OPCODE_SYSTEM_BINDING = 0; + uint8 internal constant SERVER_OPCODE_SYSTEM_UNBINDING = 1; + + uint8 internal constant SERVER_OPCODE_LENDING_SUPPLY = 2; + uint8 internal constant SERVER_OPCODE_LENDING_WITHDRAW = 3; + uint8 internal constant SERVER_OPCODE_LENDING_BORROW = 4; + uint8 internal constant SERVER_OPCODE_LENDING_REPAY = 5; + uint8 internal constant SERVER_OPCODE_LENDING_LIQUIDATE= 6; + uint8 internal constant SERVER_OPCODE_LENDING_COLLATERAL= 7; + uint8 internal constant SERVER_OPCODE_LENDING_CANCEL_COLLATERAL= 8; + + uint8 internal constant CLIENT_OPCODE_ADD_RELAYER = OFFSET + 0; // 64 + uint8 internal constant CLIENT_OPCODE_REMOVE_RELAYER = OFFSET + 1; // 65 + uint8 internal constant CLIENT_OPCODE_REGISTER_SPENDER = OFFSET + 2; // 66 + uint8 internal constant CLIENT_OPCODE_DELETE_SPENDER = OFFSET + 3; // 67 + uint8 internal constant CLIENT_OPCODE_WITHDRAW = OFFSET + 4; // 68 + + + function replayProtect( + mapping(bytes32 => bool) storage consumedMsgs, + bytes32 txUniqueIdentification, + bytes memory payload + ) internal { + bytes32 hash = keccak256(abi.encode(txUniqueIdentification,payload)); + + require(!consumedMsgs[hash], "ALREADY COMPLETED"); + + consumedMsgs[hash] = true; + } + + function remapping_opcode( + bytes memory payload, + uint8 opcode + ) public returns (bytes memory) { + bytes memory new_payload = new bytes(payload.length + 1); + + for (uint i = 0; i < payload.length; i++) { + new_payload[i] = payload[i]; + } + + new_payload[payload.length] = bytes1(opcode); + + return new_payload; + } +} \ No newline at end of file diff --git a/ethereum/contracts/omnipool/BoolAdapterPool.sol b/ethereum/contracts/omnipool/BoolAdapterPool.sol new file mode 100644 index 00000000..39d04833 --- /dev/null +++ b/ethereum/contracts/omnipool/BoolAdapterPool.sol @@ -0,0 +1,350 @@ +// Copyright (c) OmniBTC, Inc. +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.0; + +import "../libraries/LibAsset.sol"; +import "../libraries/LibPoolCodec.sol"; +import "../libraries/LibLendingCodec.sol"; +import "../libraries/LibSystemCodec.sol"; +import "../libraries/LibGovCodec.sol"; +import "../libraries/LibBoolAdapterVerify.sol"; +import "../libraries/LibBytes.sol"; +import "../../interfaces/IBoolConsumerBase.sol"; +import "../../interfaces/IBoolAdapterPool.sol"; +import "../../interfaces/IERC165.sol"; +import "../../interfaces/IBoolAnchor.sol"; +import "../../interfaces/IBoolMessenger.sol"; +import "./DolaPool.sol"; + +contract BoolAdapterPool is ERC165, IBoolConsumerBase, IBoolAdapterPool { + bytes32 public constant PURE_MESSAGE = keccak256("PURE_MESSAGE"); + + /// Storage + + // Bool Anchor address + IBoolAnchor public immutable boolAnchor; + // Dola chain id + uint16 public immutable dolaChainId; + // Dola pool + DolaPool public dolaPool; + + uint32 public immutable suiChainId; + + // Used to verify that the message has been processed + mapping(bytes32 => bool) public consumedMsgs; + // Used to verify relayer authority + mapping(address => bool) public registeredRelayers; + // Used to receive relayer fee + address[] public relayers; + + event PoolWithdrawEvent( + uint64 nonce, + uint16 sourceChainId, + uint16 dstChianId, + bytes poolAddress, + bytes receiver, + uint64 amount + ); + + constructor( + IBoolAnchor _anchor, + uint16 _dolaChainId, + DolaPool _dolaPool, + address _initialRelayer, + uint32 _suiChainId + ) { + boolAnchor = _anchor; + dolaChainId = _dolaChainId; + suiChainId = _suiChainId; + + if (address(_dolaPool) == address(0x0)) { + // First deploy pool + dolaPool = new DolaPool(_dolaChainId, address(this)); + } else { + // Upgrade + dolaPool = _dolaPool; + } + + registeredRelayers[_initialRelayer] = true; + relayers.push(_initialRelayer); + } + + /// Modifiers + + modifier onlyRelayer() { + require(registeredRelayers[msg.sender], "NOT RELAYER"); + _; + } + + /// Call by governance + + function getDolaContract() public view returns (uint256) { + return uint256(uint160(address(this))); + } + + function registerSpender(bytes memory internal_payload) internal { + LibPoolCodec.ManagePoolPayload memory payload = LibPoolCodec + .decodeManagePoolPayload(internal_payload); + require( + payload.poolCallType == LibPoolCodec.POOL_REGISTER_SPENDER, + "INVALID CALL TYPE" + ); + require(payload.dolaChainId == dolaChainId, "INVALID DOLA CHAIN"); + dolaPool.registerSpender(address(uint160(payload.dolaContract))); + } + + function deleteSpender(bytes memory internal_payload) internal { + LibPoolCodec.ManagePoolPayload memory payload = LibPoolCodec + .decodeManagePoolPayload(internal_payload); + require( + payload.poolCallType == LibPoolCodec.POOL_DELETE_SPENDER, + "INVALID CALL TYPE" + ); + require(payload.dolaChainId == dolaChainId, "INVALID DOLA CHAIN"); + dolaPool.deleteSpender(address(uint160(payload.dolaContract))); + } + + function registerRelayer(bytes memory internal_payload) internal { + LibGovCodec.RelayerPayload memory payload = LibGovCodec + .decodeRelayerPayload(internal_payload); + + require(payload.opcode == LibGovCodec.ADD_RELAYER_OPCODE); + require( + payload.relayer.dolaChainId == dolaChainId, + "INVALID DOLA CHAIN" + ); + address relayer = LibDolaTypes.dolaAddressToAddress(payload.relayer); + require(!registeredRelayers[relayer], "RELAYER ALREADY REGISTERED"); + registeredRelayers[relayer] = true; + relayers.push(relayer); + } + + function removeRelayer(bytes memory internal_payload) internal { + LibGovCodec.RelayerPayload memory payload = LibGovCodec + .decodeRelayerPayload(internal_payload); + + require(payload.opcode == LibGovCodec.REMOVE_RELAYER_OPCODE); + require( + payload.relayer.dolaChainId == dolaChainId, + "INVALID DOLA CHAIN" + ); + address relayer = LibDolaTypes.dolaAddressToAddress(payload.relayer); + + require(registeredRelayers[relayer], "RELAYER NOT REGISTERED"); + registeredRelayers[relayer] = false; + for (uint256 i = 0; i < relayers.length; i++) { + if (relayers[i] == relayer) { + relayers[i] = relayers[relayers.length - 1]; + relayers.pop(); + break; + } + } + } + + function receiveWithdraw(bytes memory internal_payload) internal { + LibPoolCodec.WithdrawPayload memory payload = LibPoolCodec + .decodeWithdrawPayload(internal_payload); + dolaPool.withdraw(payload.user, payload.amount, payload.pool); + + emit PoolWithdrawEvent( + payload.nonce, + payload.sourceChainId, + payload.pool.dolaChainId, + payload.pool.externalAddress, + payload.user.externalAddress, + payload.amount + ); + } + + function dispatch(bytes memory raw_payload) internal { + require(raw_payload.length > 0, "payload must not be empty"); + + uint8 op_code = uint8(raw_payload[raw_payload.length - 1]); + bytes memory internal_payload = LibBytes.slice( + raw_payload, + 0, + raw_payload.length - 2 + ); + + if (op_code == LibBoolAdapterVerify.CLIENT_OPCODE_ADD_RELAYER) { + registerRelayer(internal_payload); + } else if (op_code == LibBoolAdapterVerify.CLIENT_OPCODE_REMOVE_RELAYER) { + removeRelayer(internal_payload); + } else if (op_code == LibBoolAdapterVerify.CLIENT_OPCODE_REGISTER_SPENDER) { + registerSpender(internal_payload); + } else if (op_code == LibBoolAdapterVerify.CLIENT_OPCODE_DELETE_SPENDER) { + deleteSpender(internal_payload); + } else if (op_code == LibBoolAdapterVerify.CLIENT_OPCODE_WITHDRAW) { + receiveWithdraw(internal_payload); + } else { + revert("invalid client opcode"); + } + } + + + /// Call by application + + /// Send deposit by application + function sendDeposit( + address token, + uint256 amount, + uint16 appId, + bytes memory appPayload + ) external payable returns (bytes32) { + uint256 boolFee = boolMessageFee( + token, + amount, + appId, + appPayload + ); + require(msg.value >= boolFee, "FEE NOT ENOUGH"); + + // Deposit assets to the pool and perform amount checks + LibAsset.depositAsset(token, amount); + if (!LibAsset.isNativeAsset(token)) { + LibAsset.maxApproveERC20(IERC20(token), address(dolaPool), amount); + } + + bytes memory payload = dolaPool.deposit{value: msg.value - boolFee}( + token, + amount, + appId, + appPayload + ); + + return IBoolAnchor(boolAnchor).sendToMessenger{value: boolFee}( + payable(tx.origin), + PURE_MESSAGE, + "", + suiChainId, + payload + ); + } + + /// Send message that do not involve incoming or outgoing funds by application + function sendMessage( + uint16 appId, + bytes memory appPayload + ) external payable returns (bytes32) + { + uint256 boolFee = boolMessageFee( + address(0), + 0, + appId, + appPayload + ); + require(msg.value >= boolFee, "FEE NOT ENOUGH"); + + bytes memory payload = dolaPool.sendMessage(appId, appPayload); + + /// If much sufficient, return the rest to the refundAddress + return IBoolAnchor(boolAnchor).sendToMessenger{value: msg.value}( + payable(tx.origin), + PURE_MESSAGE, + "", + suiChainId, + payload + ); + } + + function getOneRelayer(uint64 nonce) external view returns (address) { + return relayers[nonce % relayers.length]; + } + + /// Get nonce + function getNonce() external returns (uint64) { + return dolaPool.getNonce(); + } + + /// Get messenger address + function boolMessenger() public view returns (address) { + return IBoolAnchor(boolAnchor).messenger(); + } + + /// Calculate the cross-chain fee to be prepaid + function boolMessageFee( + address token, + uint256 amount, + uint16 appId, + bytes memory appPayload + ) public view returns (uint256 fee) { + uint payload_len = 0; + + if (address(0) == token) { + bytes memory messagePayload = LibPoolCodec.encodeSendMessagePayload( + LibDolaTypes.addressToDolaAddress(dolaChainId, tx.origin), + appId, + appPayload + ); + + payload_len = messagePayload.length; + } else { + bytes memory poolPayload = LibPoolCodec.encodeDepositPayload( + LibDolaTypes.addressToDolaAddress(dolaChainId, token), + LibDolaTypes.addressToDolaAddress(dolaChainId, tx.origin), + LibDecimals.fixAmountDecimals( + amount, + LibAsset.queryDecimals(token) + ), + appId, + appPayload + ); + + payload_len = poolPayload.length; + } + + fee = IBoolMessenger(boolMessenger()).cptTotalFee( + address(boolAnchor), + suiChainId, + uint32(payload_len), + PURE_MESSAGE, + bytes("") + ); + } + + function getLastByte(bytes memory data) public pure returns (bytes1) { + require(data.length > 0, "Data must not be empty"); + return data[data.length - 1]; + } + + function anchor() external view returns (address) { + return address(boolAnchor); + } + + /// Our relayer will call messenger.receiveFromBool(message, signature) + /// + /// In Bool Messenger Contract + /// function receiveFromBool( + /// Message memory message, + /// bytes calldata signature + /// ) external override nonReentrant + /// + /// In the receiveFromBool function: + /// (1) verify message and signature + /// (2) duplicated txUniqueIdentification + /// (3) call back our receiveFromAnchor function + function receiveFromAnchor( + bytes32 txUniqueIdentification, + bytes memory raw_payload + ) external { + require(registeredRelayers[tx.origin], "NOT RELAYER"); + + LibBoolAdapterVerify.replayProtect( + consumedMsgs, + txUniqueIdentification, + raw_payload + ); + + dispatch(raw_payload); + } + + + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC165, IERC165) returns (bool) { + return + interfaceId == type(IBoolConsumerBase).interfaceId || + super.supportsInterface(interfaceId); + } + +} diff --git a/ethereum/contracts/omnipool/DolaPool.sol b/ethereum/contracts/omnipool/DolaPool.sol index c38ad2b1..42335023 100644 --- a/ethereum/contracts/omnipool/DolaPool.sol +++ b/ethereum/contracts/omnipool/DolaPool.sol @@ -120,6 +120,7 @@ contract DolaPool { } /// Get chain-unique nonce + /// Allow anyone to call function getNonce() external returns (uint64) { return nonce++; } diff --git a/ethereum/interfaces/IBoolAdapterPool.sol b/ethereum/interfaces/IBoolAdapterPool.sol new file mode 100644 index 00000000..1264ca8d --- /dev/null +++ b/ethereum/interfaces/IBoolAdapterPool.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IBoolAdapterPool { + function dolaChainId() external view returns (uint16); + + function getNonce() external returns (uint64); + + function getOneRelayer(uint64 nonce) external view returns (address); + + function sendDeposit( + address pool, + uint256 amount, + uint16 appId, + bytes memory appPayload + ) external payable returns (bytes32); + + function sendMessage( + uint16 appId, + bytes memory appPayload + ) external payable returns (bytes32); +} diff --git a/ethereum/interfaces/IBoolAnchor.sol b/ethereum/interfaces/IBoolAnchor.sol new file mode 100644 index 00000000..d48fd6ca --- /dev/null +++ b/ethereum/interfaces/IBoolAnchor.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IBoolAnchor { + function messenger() external view returns (address); + + function sendToMessenger( + address payable refundAddress, + bytes32 crossType, + bytes memory extraFeed, + uint32 dstChainId, + bytes calldata payload + ) external payable returns (bytes32 txUniqueIdentification); +} \ No newline at end of file diff --git a/ethereum/interfaces/IBoolConsumerBase.sol b/ethereum/interfaces/IBoolConsumerBase.sol new file mode 100644 index 00000000..a0c811e5 --- /dev/null +++ b/ethereum/interfaces/IBoolConsumerBase.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./IERC165.sol"; + +interface IBoolConsumerBase is IERC165 { + function anchor() external view returns (address); + + function receiveFromAnchor( + bytes32 txUniqueIdentification, + bytes memory payload + ) external; +} \ No newline at end of file diff --git a/ethereum/interfaces/IBoolMessenger.sol b/ethereum/interfaces/IBoolMessenger.sol new file mode 100644 index 00000000..358f67c9 --- /dev/null +++ b/ethereum/interfaces/IBoolMessenger.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IBoolMessenger { + function cptTotalFee( + address srcAnchor, + uint32 dstChainId, + uint32 payloadSize, + bytes32 crossType, + bytes memory extraFeed + ) external view returns (uint256 feeInNative); +} \ No newline at end of file diff --git a/ethereum/interfaces/IERC165.sol b/ethereum/interfaces/IERC165.sol new file mode 100644 index 00000000..0d0d1088 --- /dev/null +++ b/ethereum/interfaces/IERC165.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IERC165 { + function supportsInterface(bytes4 interfaceId) external view returns (bool); +} + +abstract contract ERC165 is IERC165 { + function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { + return interfaceId == type(IERC165).interfaceId; + } +} \ No newline at end of file diff --git a/ethereum/scripts/dola_ethereum_sdk/deploy.py b/ethereum/scripts/dola_ethereum_sdk/deploy.py index 3fab7145..103645a4 100644 --- a/ethereum/scripts/dola_ethereum_sdk/deploy.py +++ b/ethereum/scripts/dola_ethereum_sdk/deploy.py @@ -67,6 +67,72 @@ def deploy(): with open(path, "w") as f: yaml.safe_dump(config_file, f) +def deploy_bool_adapter(dola_pool = "0x0000000000000000000000000000000000000000"): + account = get_account() + cur_net = network.show_active() + print(f"Current network:{cur_net}, account:{account}") + + src_bool_anchor = config["networks"][cur_net]["src_bool_anchor"] + src_dola_chainid = config["networks"][cur_net]["src_dola_chainid"] + dst_bool_chainid = config["networks"][cur_net]["dst_bool_chainid"] + + + print(f"deploy bool adapter pool: src_bool_anchor={src_bool_anchor}") + bool_adapter_pool = DOLA_CONFIG["DOLA_ETHEREUM_PROJECT"]["BoolAdapterPool"].deploy( + src_bool_anchor, + src_dola_chainid, + dola_pool, + account.address, + dst_bool_chainid, + {'from': account} + ) + dolaPool = str(bool_adapter_pool.dolaPool()) + print(f"dolaPool={dolaPool}") + + print("deploy LibBoolAdapterVerify...") + lib_bool_adapter_verify = DOLA_CONFIG["DOLA_ETHEREUM_PROJECT"]["LibBoolAdapterVerify"].deploy( + {'from': account} + ) + print(f"lib_bool_adapter_verify={lib_bool_adapter_verify}") + + print("deploy lending_bool portal...") + lending_portal = DOLA_CONFIG["DOLA_ETHEREUM_PROJECT"]["LendingPortalBool"].deploy( + bool_adapter_pool.address, + {'from': account} + ) + + print("deploy system_bool portal...") + system_portal = DOLA_CONFIG["DOLA_ETHEREUM_PROJECT"]["SystemPortalBool"].deploy( + bool_adapter_pool.address, + {'from': account} + ) + + path = DOLA_CONFIG["DOLA_PROJECT_PATH"].joinpath('ethereum/brownie-config.yaml') + with open(path, "r") as f: + config_file = yaml.safe_load(f) + + config_file["networks"][cur_net]["bool_adapter_pool"] = { + "latest": bool_adapter_pool.address + } + config_file["networks"][cur_net]["lending_portal_bool"] = lending_portal.address + config_file["networks"][cur_net]["system_portal_bool"] = system_portal.address + config_file["networks"][cur_net]["dola_pool"] = dolaPool + + + if "test" in cur_net: + wbtc = deploy_token("WBTC") + + usdt = deploy_token("USDT") + + usdc = deploy_token("USDC") + + config_file["networks"][cur_net]["wbtc"] = wbtc.address + config_file["networks"][cur_net]["usdt"] = usdt.address + config_file["networks"][cur_net]["usdc"] = usdc.address + + with open(path, "w") as f: + yaml.safe_dump(config_file, f) + def redeploy(): account = get_account() @@ -118,7 +184,6 @@ def deploy_token(token_name="USDT"): token_name, token_name, {'from': account} ) - if __name__ == "__main__": set_ethereum_network("base-main") deploy() diff --git a/ethereum/scripts/dola_ethereum_sdk/init.py b/ethereum/scripts/dola_ethereum_sdk/init.py index 62bd8236..3627bb7b 100644 --- a/ethereum/scripts/dola_ethereum_sdk/init.py +++ b/ethereum/scripts/dola_ethereum_sdk/init.py @@ -228,6 +228,38 @@ def fallback_endpoints_web3(network, external_endpoint=None): return web3.Web3(FallbackProvider(endpoints)) +def bool_adapter_init(net="bevm-test"): + print(f"bool_adapter_init: {net}") + anchor_address = config["networks"][net]["src_bool_anchor"] + anchor = load.bool_anchor_package(anchor_address) + + account = get_account() + + # update consumer + consumer = load.bool_adapter_pool_package(net).address + anchor.updateConsumer( + consumer, + {"from": account} + ) + + print(f"src_anchor={anchor_address}, src_consumer={consumer}") + + # update path + + remote_chainid = config["networks"][net]["dst_bool_chainid"] + remote_anchor = bytes.fromhex(config["networks"][net]["dst_bool_anchor"].replace("0x", "")) + + if not anchor.isPathEnabled(remote_chainid): + anchor.batchUpdateRemoteAnchors( + [remote_chainid], + [remote_anchor], + {"from": account} + ) + + _remote_anchor = anchor.fetchRemoteAnchor(remote_chainid) + print(f"remote_chainid={remote_chainid}, remote_anchor={_remote_anchor}") + + if __name__ == "__main__": net = "polygon-main" set_ethereum_network(net) diff --git a/ethereum/scripts/dola_ethereum_sdk/load.py b/ethereum/scripts/dola_ethereum_sdk/load.py index de2c4b01..d116a6c2 100644 --- a/ethereum/scripts/dola_ethereum_sdk/load.py +++ b/ethereum/scripts/dola_ethereum_sdk/load.py @@ -1,3 +1,4 @@ +import json from brownie import Contract from dola_ethereum_sdk import DOLA_CONFIG, config @@ -15,6 +16,12 @@ def wormhole_adapter_pool_package(network, package_address=None): return Contract.from_abi("WormholeAdapterPool", package_address, DOLA_CONFIG["DOLA_ETHEREUM_PROJECT"]["WormholeAdapterPool"].abi) +def bool_adapter_pool_package(network, package_address=None): + if package_address is None: + package_address = config["networks"][network]["bool_adapter_pool"]["latest"] + return Contract.from_abi("BoolAdapterPool", package_address, + DOLA_CONFIG["DOLA_ETHEREUM_PROJECT"]["BoolAdapterPool"].abi) + def dola_pool_package(network, package_address=None): if package_address is None: @@ -34,6 +41,17 @@ def system_portal_package(network): return Contract.from_abi("SystemPortal", package_address, DOLA_CONFIG["DOLA_ETHEREUM_PROJECT"]["SystemPortal"].abi) +def lending_portal_bool_package(network): + package_address = config["networks"][network]["lending_portal_bool"] + return Contract.from_abi("LendingPortal", package_address, + DOLA_CONFIG["DOLA_ETHEREUM_PROJECT"]["LendingPortalBool"].abi) + + +def system_portal_bool_package(network): + package_address = config["networks"][network]["system_portal_bool"] + return Contract.from_abi("SystemPortal", package_address, + DOLA_CONFIG["DOLA_ETHEREUM_PROJECT"]["SystemPortalBool"].abi) + def test_coins_package(): return DOLA_CONFIG["DOLA_ETHEREUM_PROJECT"]["MockToken"][-1] @@ -46,3 +64,533 @@ def erc20_package(package_address): def w3_erc20_package(w3_eth, package_address): return w3_eth.contract(package_address, abi=DOLA_CONFIG["DOLA_ETHEREUM_PROJECT"]["ERC20"].abi) + + +def bool_messenger_package(package_address): + messenger_abi_json = """[ + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "txUniqueIdentification", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "crossType", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "srcAnchor", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "bnExtraFeed", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "dstAnchor", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "payload", + "type": "bytes" + } + ], + "internalType": "struct IMsgReceiver.Message", + "name": "message", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "receiveFromBool", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +]""" + + messenger_abi = json.loads(messenger_abi_json) + + return Contract.from_abi("Messenger", package_address, messenger_abi) + +def bool_anchor_package(anchor_address): + anchor_abi_json = """ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "name_", + "type": "string" + }, + { + "internalType": "address", + "name": "deployer_", + "type": "address" + }, + { + "internalType": "address", + "name": "committee_", + "type": "address" + }, + { + "internalType": "address", + "name": "messenger_", + "type": "address" + }, + { + "internalType": "address", + "name": "factory_", + "type": "address" + }, + { + "internalType": "address", + "name": "anchorLibrary_", + "type": "address" + }, + { + "internalType": "address", + "name": "relayer_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "actualFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "requiredFee", + "type": "uint256" + } + ], + "name": "INSUFFICIENT_RELAYER_FEE", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "pathId", + "type": "uint32" + } + ], + "name": "INVALID_PATH", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "wrongConsumer", + "type": "address" + } + ], + "name": "NOT_CONSUMER", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "NOT_MANAGER", + "type": "error" + }, + { + "inputs": [], + "name": "NULL_ADDRESS", + "type": "error" + }, + { + "inputs": [], + "name": "NULL_ANCHOR", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousConsumer", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newConsumer", + "type": "address" + } + ], + "name": "ConsumerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousManager", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newManager", + "type": "address" + } + ], + "name": "ManagerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txUniqueIdentification", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "reason", + "type": "bytes" + } + ], + "name": "MessageDeliverFailed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousRelayer", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newRelayer", + "type": "address" + } + ], + "name": "RelayerUpdated", + "type": "event" + }, + { + "inputs": [], + "name": "anchorLibrary", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32[]", + "name": "remoteChainIds", + "type": "uint32[]" + }, + { + "internalType": "bytes32[]", + "name": "remoteAnchors", + "type": "bytes32[]" + } + ], + "name": "batchUpdateRemoteAnchors", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "committee", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "consumer", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "remoteChainId", + "type": "uint32" + } + ], + "name": "fetchRemoteAnchor", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "remoteChainId", + "type": "uint32" + } + ], + "name": "isPathEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "manager", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "messenger", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "txUniqueIdentification", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "payload", + "type": "bytes" + } + ], + "name": "receiveFromMessenger", + "outputs": [ + { + "internalType": "enum IAnchor.MessageStatus", + "name": "status", + "type": "uint8" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "relayer", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "refundAddress", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "crossType", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "extraFeed", + "type": "bytes" + }, + { + "internalType": "uint32", + "name": "dstChainId", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "payload", + "type": "bytes" + } + ], + "name": "sendToMessenger", + "outputs": [ + { + "internalType": "bytes32", + "name": "txUniqueIdentification", + "type": "bytes32" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "totalRemotePaths", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newConsumer", + "type": "address" + } + ], + "name": "updateConsumer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newManager", + "type": "address" + } + ], + "name": "updateManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newRelayer", + "type": "address" + } + ], + "name": "updateRelayer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] +""" + + anchor_abi = json.loads(anchor_abi_json) + + return Contract.from_abi("Anchor", anchor_address, anchor_abi) \ No newline at end of file diff --git a/sui/brownie-config.yaml b/sui/brownie-config.yaml index 08c62243..d87d9ba6 100644 --- a/sui/brownie-config.yaml +++ b/sui/brownie-config.yaml @@ -48,6 +48,7 @@ networks: USDT/USD: '0x2b89b9dc8fdf9f34709a5b106b472f0f39bb6ca9ce04b0fd7f2e971688e2e53b' packages: dola_protocol: + latest: '0xc26491612cdb386cb943ae92764d94d85f1f2c6fb1890ab6a2d5687521626c21' origin: '0x826915f8ca6d11597dfe6599b8aa02a4c08bd8d39674855254a06ee83fe7220e' v_1_0_1: '0xc5b2a5049cd71586362d0c6a38e34cfaae7ea9ce6d5401a350506a15f817bf72' v_1_0_2: '0x526352c8d904fdd4d61f0a2223e132a794723e672d97cd342455eb1e4dc7e52d' @@ -58,7 +59,6 @@ networks: v_1_0_7: '0x2c674f67f73bb32ea94123e4a0c509de9f529a2423818b275d9a9019ad83cd04' v_1_0_8: '0x1f61d9c4a266a24cd53fabd902c3e012e221c2a223bbb0532229af3b8ac8ca65' v_1_0_9: '0xc26491612cdb386cb943ae92764d94d85f1f2c6fb1890ab6a2d5687521626c21' - latest: '0xc26491612cdb386cb943ae92764d94d85f1f2c6fb1890ab6a2d5687521626c21' external_interfaces: '0x6c3367d4eac668f8a7944b65f5185804c9573e15594e6198ef67b1c8e8fc0e54' genesis_proposal: '0x693828fbf1322d43f23ad365ab27b7d6f6649133aa3761ff55be6380e53c1b70' pyth: '0x00b53b0f4174108627fbee72e2498b58d6a2714cded53fac537034c220d26302' @@ -79,6 +79,20 @@ networks: pool_weight: 1 pyth_service_url: https://xc-mainnet.pyth.network reserves: + ARB: + base_borrow_rate: 0 + borrow_cap_ceiling: 0 + borrow_coefficient: 1.33 + borrow_rate_slope1: 0.07 + borrow_rate_slope2: 3 + borrowable_in_isolation: false + collateral_coefficient: 0.66 + dola_pool_id: 6 + is_isolated_asset: false + optimal_utilization: 0.8 + supply_cap_ceiling: 1000000 + treasury: 0 + treasury_factor: 0.23 BTC: base_borrow_rate: 0 borrow_cap_ceiling: 0 @@ -107,48 +121,34 @@ networks: supply_cap_ceiling: 500 treasury: 0 treasury_factor: 0.23 - OP: + MATIC: base_borrow_rate: 0 borrow_cap_ceiling: 0 - borrow_coefficient: 1.09 - borrow_rate_slope1: 0.07 - borrow_rate_slope2: 3 + borrow_coefficient: 1.33 + borrow_rate_slope1: 0.061 + borrow_rate_slope2: 1 borrowable_in_isolation: false - collateral_coefficient: 0.88 - dola_pool_id: 7 + collateral_coefficient: 0.66 + dola_pool_id: 5 is_isolated_asset: false - optimal_utilization: 0.8 + optimal_utilization: 0.75 supply_cap_ceiling: 1000000 treasury: 0 treasury_factor: 0.23 - ARB: + OP: base_borrow_rate: 0 borrow_cap_ceiling: 0 - borrow_coefficient: 1.33 + borrow_coefficient: 1.09 borrow_rate_slope1: 0.07 borrow_rate_slope2: 3 borrowable_in_isolation: false - collateral_coefficient: 0.66 - dola_pool_id: 6 + collateral_coefficient: 0.88 + dola_pool_id: 7 is_isolated_asset: false optimal_utilization: 0.8 supply_cap_ceiling: 1000000 treasury: 0 treasury_factor: 0.23 - MATIC: - base_borrow_rate: 0 - borrow_cap_ceiling: 0 - borrow_coefficient: 1.33 - borrow_rate_slope1: 0.061 - borrow_rate_slope2: 1 - borrowable_in_isolation: false - collateral_coefficient: 0.66 - dola_pool_id: 5 - is_isolated_asset: false - optimal_utilization: 0.75 - supply_cap_ceiling: 1000000 - treasury: 0 - treasury_factor: 0.23 SUI: base_borrow_rate: 0 borrow_cap_ceiling: 0 @@ -218,27 +218,56 @@ networks: coin_type: 0xc060006111016b8a020ad5b33834984a437aaa7d3c74c18e09a95d48aceab08c::coin::COIN decimals: 6 dola_pool_id: 1 - wormhole_url: https://wormhole-v2-mainnet-api.certus.one wormhole_scan_url: https://api.wormscan.io/api/v1/ + wormhole_url: https://wormhole-v2-mainnet-api.certus.one sui-testnet: - node_url: https://sui-testnet-endpoint.blockvision.org:443 + bool_network: + chains: + bevm-testnet: + anchor: '0x45395fc32da85a18285639f881cc1b7785c91091' + chainid: 1502 + cid: 368 + consumer": '0xb62bb643DB4e67AD2c1D90cC6Fb974008B0cbE79' + messenger: '0xF68F872F0DDE0Ec1Ba8c28EeD9d0674760aA8eB1' + remote_anchor: '0x65da07243a0579ae918f74aad5fbd1fedf13006163cbf5564fbaccbd5ec5741c' + remote_chainid: 1918346523 + sui-testnet: + anchor: '0x65da07243a0579ae918f74aad5fbd1fedf13006163cbf5564fbaccbd5ec5741c' + chainid: 1918346523 + cid: 370 + remote_anchor: '0x45395fc32da85a18285639f881cc1b7785c91091' + remote_chainid: 1502 + cids: + 368: + chain: bevm-testnet + committee_key: '0x04c12f387800f95975fa7c988d0ce942de30d8eac95ff187742ebd562a4fe2dff2fc98faaac07b103b5cbb36d9e5e00334c73b33653a28239dddbc738c6d5ba521' + key_type: ecdsa + 370: + chain: sui-testnet + committee_key: '0x28a9f4b0490113ac63e4082d263aa6b88b64ef7a594a0b977205495b0fa86e87' + key_type: eddsa + committee_key: '0x28a9f4b0490113ac63e4082d263aa6b88b64ef7a594a0b977205495b0fa86e87' + core_state: '0xcd4af4fcdb43af55f0afbe4a4a105efb8191584710b21207d4f29c1625aa232b' + global_state: '0x3ff7bed6107de126102bfe8ec402b816537b3690c8ca541a5d6d9522fc2c250a' + node_url: wss://test-rpc-node-ws.bool.network + node_url: https://fullnode.testnet.sui.io objects: Clock: '0x0000000000000000000000000000000000000000000000000000000000000006' - CoreState: '0x203ed644a34bdead51b69a5dfe384f66098bd0a1329108d5abe365211fbb3635' - GovernanceGenesis: '0xb120ce4627baf7bf6f3c83a43c9ef91644a451d1c677a851c0e129fa03fb1e09' - GovernanceInfo: '0xa696a2de2a3d6b29129796c6b24835c15d7aa63c00899bb7f6af002606947ce5' - LendingPortal: '0xcc1956d488781eb6b48b52d92a2992fbd75166b83d0f11c1ec1cf404b54576a1' + CoreState: '0x80dc9ddc24d693d33cdb2737684fb859dcccf74a75aeecfc54c49197787cbc6f' + GovernanceGenesis: '0x468b3342e4f918d35b6c981fd5142965706a16fcf0f2f3295ff03f83773ac17e' + GovernanceInfo: '0x8955e740aa17a3069efe5dac5cca5b3f931d8769507595ff3cf8f008f7ade317' + LendingPortal: '0x527b967d70c59e38b06cf973f64b32932efa0a7978176cef478f751122b535a9' LendingStorage: '0x301459172ff7023416c0ed132df419f67693806a5a8a0397f8a42f5681c30973' Pool: '0x283f712a6c6a9361132e2d75aee4b4499f98892a3b8cfd4a7244ad6862c62aa9' - PoolManagerInfo: '0xae4165234c13256599879183a2bd28000a10a216f2ec2ad60859b4552c269a09' + PoolManagerInfo: '0xd2f8c7a8aad3998530708c85407d77eec4f205766e0afce4a4cba64e4ffbb3cd' PoolState: '0x52c9fe49e061f5d10c163ccc1996906fda4f3bdec40b9bb4ee36bcdacaee65c7' - PriceOracle: '0xaeb51419b4e937355fae227ac70c5f3bce944ad6a0e38812b7e40ca1ed3fa9cb' - PythState: '0xd3e79c2c083b934e78b3bd58a490ec6b092561954da6e7322e1e2b3c8abfddc0' + PriceOracle: '0x984766f2018c9257f72ba6fff253afb0bbfc7d35e193f747dfad359ca370c7f8' + PythState: '0x2d82612a354f0b7e52809fc2845642911c7190404620cec8688f68808f8800d8' SUI: 0000000000000000000000000000000000000000000000000000000000000002::sui::SUI - SystemPortal: '0xd8ee49f63b502a10be5c9ceb9dadb1f1f184bcc71aece57786c2e37593709da8' + SystemPortal: '0xe4aefa6e4d5e9ed2227c23f8044ec86d65476c8f071fd98139b50cc12938dcd0' SystemStorage: '0x18177724587ca69a9c8fc6f4c15e5b4b4d1fee1374ade62a2707f2069021a7f3' - UserManagerInfo: '0xea1a84e3b17bcf9beb44d0b806df92cc4fb345bb79e7c59f3fda30b85bdb1301' - WormholeState: '0x31358d198147da50db32eda2562951d53973a0c0ad5ed738e9b17d88b213d790' + UserManagerInfo: '0x771eeda80aab0e8da790e854ab63e6667ea9f3ed5ced6c774d3fe22860a86ccb' + WormholeState: '0xebba4cc4d614f7a7cdbe883acc76d1cc767922bc96778e7b68be0d15fce27c02' oracle: feed_id: APT/USD: '0x44a93dddd8effa54ea51076c4e851b6cbbfd938e82eb90197de38fe8876bb66e' @@ -253,12 +282,12 @@ networks: USDT/USD: '0x1fc18861232290221461220bd4e2acd1dcdfbc89c84092c93c18bdc7756c1588' packages: dola_protocol: - origin: '0xdab5bbcf6b6aa9e1ebf4c8636505428a9c2a4cbe75d84d640cb6e7abfe8ef5e2' - latest: '0x8ab018a51be9817daa2a6c535a714e13d742ffa49e026f324747d5a7d9883378' - external_interfaces: '0xf68dfe40252482395491f9a49b2ce19b0aadba3960e3c19c37fb803e42c9d303' - genesis_proposal: '0xb03e33e795a4ba328fe4e90252fc066ae95aaf546d967a94c0833e1031bc33eb' - pyth: '0x431c1cfb9a4da32c77810a1c48aa19cc2edb03010281e0fe411b4b3b38589df1' - wormhole: '0xf47329f4344f3bf0f8e436e2f7b485466cff300f12a166563995d3888c296a94' + latest: '0x8b0d40e4a40eb90c6d6002bca4962e5cf890b7e9f16efbdfe64d3ec1bf2d598e' + origin: '0x8b0d40e4a40eb90c6d6002bca4962e5cf890b7e9f16efbdfe64d3ec1bf2d598e' + external_interfaces: '0xe011b662f0553e00fee7433be1cac962f92875a2f15fed06482799fd79217b99' + genesis_proposal: '0x5dbe8b093a0fc41d4fad58ed47104f84da75844a4c1c30232214980fa647efb6' + pyth: '0xe76d8a37d4132278a7a752183e90e04890b9e7d0f6657eadb68821609a2a56a3' + wormhole: '0xcc029e2810f17f9f43f52262f40026a71fbdca40ed3803ad2884994361910b7e' pools: SUI: dola_chain_id: 0 @@ -385,14 +414,14 @@ networks: coin_type: 0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI decimals: 9 dola_pool_id: 3 - wormhole_url: https://wormhole-v2-testnet-api.certus.one wormhole_scan_url: https://api.testnet.wormscan.io/api/v1/ + wormhole_url: https://wormhole-v2-testnet-api.certus.one sui_wallets: from_mnemonic: - TestAccount: ${DEFAULT} - OracleGuard: ${LENDING_ORACLE_GUARD} LendingCore1: ${LENDING_CORE1} LendingCore2: ${LENDING_CORE2} LendingCore3: ${LENDING_CORE3} LendingPool: ${LENDING_POOL} Liquidator: ${LENDING_LIQUIDATE} + OracleGuard: ${LENDING_ORACLE_GUARD} + TestAccount: ${DEFAULT} diff --git a/sui/dola_protocol/Move.lock b/sui/dola_protocol/Move.lock index d9bf8612..88fb1bb5 100644 --- a/sui/dola_protocol/Move.lock +++ b/sui/dola_protocol/Move.lock @@ -7,6 +7,7 @@ dependencies = [ { name = "Pyth" }, { name = "Sui" }, { name = "Wormhole" }, + { name = "boolamt" }, ] [[move.package]] @@ -32,7 +33,15 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } + +dependencies = [ + { name = "Sui" }, +] + +[[move.package]] +name = "boolamt" +source = { local = "../boolamt" } dependencies = [ { name = "Sui" }, diff --git a/sui/dola_protocol/Move.toml b/sui/dola_protocol/Move.toml index 6a33a5d6..d08ff50f 100644 --- a/sui/dola_protocol/Move.toml +++ b/sui/dola_protocol/Move.toml @@ -7,6 +7,7 @@ published-at = "0x1f61d9c4a266a24cd53fabd902c3e012e221c2a223bbb0532229af3b8ac8ca dola_protocol = "0x0" wormhole = "0x5306f64e312b581766351c07af79c72fcb1cd25147157fdc2f8ad76de9a3fb6a" pyth = "0x00b53b0f4174108627fbee72e2498b58d6a2714cded53fac537034c220d26302" +boolamt = "0xa1f98a4fa3e7305b1772b45623f3338a73b975a2c6bf0053d93bce03ac10cc5c" [dependencies.Sui] git = "https://github.com/MystenLabs/sui.git" @@ -16,10 +17,13 @@ rev = "09b2081498366df936abae26eea4b2d5cafb2788" [dependencies.Wormhole] git = "https://github.com/wormhole-foundation/wormhole.git" subdir = "sui/wormhole" -rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837" +rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad" override = true [dependencies.Pyth] git = "https://github.com/pyth-network/pyth-crosschain.git" subdir = "target_chains/sui/contracts" rev = "7dab308f961746890faf1ac0b52e283b31112bf6" + +[dependencies.boolamt] +local = "../boolamt" diff --git a/sui/dola_protocol/sources/bool_adapter_core/bool_adapter_core.move b/sui/dola_protocol/sources/bool_adapter_core/bool_adapter_core.move new file mode 100644 index 00000000..9b345e1b --- /dev/null +++ b/sui/dola_protocol/sources/bool_adapter_core/bool_adapter_core.move @@ -0,0 +1,559 @@ +// Copyright (c) OmniBTC, Inc. +// SPDX-License-Identifier: GPL-3.0 + +/// Boolnet bridge adapter, this module is responsible for adapting boolnet to pass messages for settlement center +/// applications (such as lending core). The usage of this module are: 1) Update the status of user_manager and +/// pool_manager; 2) Verify message signature, decode PoolPaload, and pass it to the correct application +module dola_protocol::bool_adapter_core { + use std::vector; + + use sui::coin::{Self, Coin}; + use sui::dynamic_field; + use sui::dynamic_object_field; + use sui::event; + use sui::object::{Self, UID}; + use sui::table; + use sui::sui::SUI; + use sui::transfer; + use sui::tx_context; + use sui::tx_context::{TxContext, sender}; + + use boolamt::anchor::{AnchorCap, GlobalState, enable_path}; + use boolamt::consumer; + use boolamt::messenger; + + use dola_protocol::app_manager::{Self, AppCap}; + use dola_protocol::dola_address; + use dola_protocol::dola_address::{DolaAddress, get_dola_chain_id}; + use dola_protocol::genesis::GovernanceCap; + use dola_protocol::pool_codec; + use dola_protocol::pool_manager::{Self, PoolManagerInfo}; + use dola_protocol::remote_gov_codec; + use dola_protocol::user_manager::{Self, UserManagerInfo}; + use dola_protocol::bool_adapter_verify::{ + parse_verify_and_replay_protect, + remapping_opcode, + client_opcode_add_relayer, + client_opcode_remove_relayer, + client_opcode_register_spender, + client_opcode_delete_spender, + client_opcode_withdraw + }; + + + friend dola_protocol::system_core_bool_adapter; + friend dola_protocol::lending_core_bool_adapter; + + /// Errors + // Bridge is not registered + const ENOT_REGISTERED_EMITTER: u64 = 0; + + // Bridge has registered + const EHAS_REGISTERED_EMITTER: u64 = 1; + + // Invalid App + const EINVALID_APP: u64 = 2; + + const EDUPLICATED_RELAYER: u64 = 3; + + const ENOT_RELAYER: u64 = 4; + + const ERELAYER_NOT_INIT: u64 = 5; + + const ERELAYER_NOT_EXIST: u64 = 6; + + const EVAA_HAS_EXPIRED: u64 = 7; + + + /// `bool_bridge_adapter` adapts to boolnet, enabling cross-chain messaging. + /// For messsage data, the following validations are required. + /// For boolamt official library: + /// 1) verify the signature. + /// 2) make sure it comes from the correct path(dst_chain_id, dst_anchor) + /// 3) make sure the data has not been processed by tx_unique_identification; + /// For bool_bridge_adapter itself: + /// 1) make sure the caller is from the correct application by app_id from pool payload. + struct CoreState has key, store { + id: UID, + // dola_chain_id(dola config) => dst_chain_id(boolnet config) + chain_id_map: table::Table + } + + /// Move does not have a contract address, boolnet uses the AnchorCap + /// to represent the send address of this contract + struct BoolAnchorCap has copy, drop, store {} + + /// Only certain users are allowed to act as Relayer + struct Relayer has copy, drop, store {} + + /// Events + + /// Event for register spender + struct RegisterSpender has copy, drop { + dola_chain_id: u16, + dola_contract: u256 + } + + /// Event for delete spender + struct DeleteSpender has copy, drop { + dola_chain_id: u16, + dola_contract: u256 + } + + /// Event for add relayer + struct AddRelayer has drop, copy { + new_relayer: address + } + + /// Event for remove relayer + struct RemoveRelayer has drop, copy { + removed_relayer: address + } + + /// === Governance Functions === + + /// Initializing caps of PoolManager and UserManager through governance + public fun initialize_cap_with_governance( + governance_cap: &GovernanceCap, + bool_anchor_cap: AnchorCap, + init_relayer: address, + ctx: &mut TxContext + ) { + let core_state = CoreState { + id: object::new(ctx), + chain_id_map: table::new(ctx) + }; + + dynamic_object_field::add( + &mut core_state.id, + BoolAnchorCap {}, + bool_anchor_cap + ); + + add_relayer( + governance_cap, + &mut core_state, + init_relayer + ); + + transfer::public_share_object(core_state); + } + + /// Call by governance + + public fun set_anchor_cap( + _: &GovernanceCap, + core_state: &mut CoreState, + bool_anchor_cap: AnchorCap + ) { + dynamic_object_field::add( + &mut core_state.id, + BoolAnchorCap {}, + bool_anchor_cap + ); + } + + public fun release_anchor_cap( + _: &GovernanceCap, + core_state: &mut CoreState, + receiver: address + ) { + let bool_anchor_cap = dynamic_object_field::remove( + &mut core_state.id, + BoolAnchorCap {} + ); + + transfer::public_transfer(bool_anchor_cap, receiver) + } + + /// Register path on boolamt contract + public fun register_path( + _: &GovernanceCap, + core_state: &mut CoreState, + dola_chain_id: u16, + dst_chain_id: u32, + dst_anchor: address, + bool_global_state: &mut GlobalState, + ) { + enable_path( + dst_chain_id, + dst_anchor, + get_anchor_cap(&core_state.id), + bool_global_state + ); + + table::add(&mut core_state.chain_id_map, dola_chain_id, dst_chain_id) + } + + /// Register spender for remote bridge through governance + public fun remote_register_spender( + _: &GovernanceCap, + bool_state: &mut GlobalState, + core_state: &mut CoreState, + dola_chain_id: u16, + dola_contract: u256, + bool_message_fee: Coin, + ctx: &mut TxContext + ) { + let payload = pool_codec::encode_manage_pool_payload( + dola_chain_id, + dola_contract, + pool_codec::get_register_spender_type() + ); + + remapping_opcode(&mut payload, client_opcode_register_spender()); + + let dst_chain_id = table::borrow( + &core_state.chain_id_map, + dola_chain_id + ); + + send_to_bool( + *dst_chain_id, + payload, + bool_message_fee, + get_anchor_cap(&core_state.id), + bool_state, + ctx + ); + + event::emit(RegisterSpender { dola_chain_id, dola_contract }); + } + + /// Delete spender for remote bridge through governance + public fun remote_delete_spender( + _: &GovernanceCap, + bool_state: &mut GlobalState, + core_state: &mut CoreState, + dola_chain_id: u16, + dola_contract: u256, + bool_message_fee: Coin, + ctx: &mut TxContext + ) { + let payload = pool_codec::encode_manage_pool_payload( + dola_chain_id, + dola_contract, + pool_codec::get_delete_spender_type() + ); + + remapping_opcode(&mut payload, client_opcode_delete_spender()); + + let dst_chain_id = table::borrow( + &core_state.chain_id_map, + dola_chain_id + ); + + send_to_bool( + *dst_chain_id, + payload, + bool_message_fee, + get_anchor_cap(&core_state.id), + bool_state, + ctx + ); + + event::emit(DeleteSpender { dola_chain_id, dola_contract }); + } + + public fun remote_add_relayer( + _: &GovernanceCap, + bool_state: &mut GlobalState, + core_state: &mut CoreState, + dola_chain_id: u16, + relayer: vector, + bool_message_fee: Coin, + ctx: &mut TxContext + ) { + let payload = remote_gov_codec::encode_relayer_payload( + dola_address::create_dola_address(dola_chain_id, relayer), + remote_gov_codec::get_add_relayer_opcode() + ); + + remapping_opcode(&mut payload, client_opcode_add_relayer()); + + let dst_chain_id = table::borrow( + &core_state.chain_id_map, + dola_chain_id + ); + + send_to_bool( + *dst_chain_id, + payload, + bool_message_fee, + get_anchor_cap(&core_state.id), + bool_state, + ctx + ); + } + + public fun remote_remove_relayer( + _: &GovernanceCap, + bool_state: &mut GlobalState, + core_state: &mut CoreState, + dola_chain_id: u16, + relayer: vector, + bool_message_fee: Coin, + ctx: &mut TxContext + ) { + let payload = remote_gov_codec::encode_relayer_payload( + dola_address::create_dola_address(dola_chain_id, relayer), + remote_gov_codec::get_remove_relayer_opcode() + ); + + remapping_opcode(&mut payload, client_opcode_remove_relayer()); + + let dst_chain_id = table::borrow( + &core_state.chain_id_map, + dola_chain_id + ); + + send_to_bool( + *dst_chain_id, + payload, + bool_message_fee, + get_anchor_cap(&core_state.id), + bool_state, + ctx + ); + } + + public fun add_relayer( + _: &GovernanceCap, + core_state: &mut CoreState, + relayer: address + ) { + if (dynamic_field::exists_with_type>(&mut core_state.id, Relayer {})) { + let relayers = dynamic_field::borrow_mut>(&mut core_state.id, Relayer {}); + assert!(!vector::contains(relayers, &relayer), EDUPLICATED_RELAYER); + vector::push_back(relayers, relayer); + } else { + dynamic_field::add>(&mut core_state.id, Relayer {}, vector[relayer]); + }; + event::emit(AddRelayer { + new_relayer: relayer + }); + } + + public fun remove_relayer( + _: &GovernanceCap, + core_state: &mut CoreState, + relayer: address + ) { + assert!( + dynamic_field::exists_with_type>(&mut core_state.id, Relayer {}), + ERELAYER_NOT_INIT + ); + let relayers = dynamic_field::borrow_mut>(&mut core_state.id, Relayer {}); + assert!(vector::contains(relayers, &relayer), ERELAYER_NOT_EXIST); + let (_, index) = vector::index_of(relayers, &relayer); + vector::remove(relayers, index); + event::emit(RemoveRelayer { + removed_relayer: relayer + }); + } + + public fun get_bool_chain_id( + core_state: &CoreState, + dola_chain_id: u16 + ): u32 { + let bool_chain_id = table::borrow( + &core_state.chain_id_map, + dola_chain_id + ); + + return *bool_chain_id + } + + /// === Friend Functions === + + /// Receive message without funding + public(friend) fun receive_message( + core_state: &mut CoreState, + bool_state: &mut GlobalState, + message_raw: vector, + signature: vector, + app_cap: &AppCap, + ctx: &mut TxContext + ): (DolaAddress, vector) { + check_relayer(core_state, ctx); + + let payload_without_opcode = parse_verify_and_replay_protect( + message_raw, + signature, + get_anchor_cap(&core_state.id), + bool_state + ); + + let (user_address, app_id, _, app_payload) = + pool_codec::decode_send_message_payload(payload_without_opcode); + + // Ensure that vaa is delivered to the correct application + assert!(app_manager::get_app_id(app_cap) == app_id, EINVALID_APP); + (user_address, app_payload) + } + + /// Receive deposit on sui network + public(friend) fun receive_deposit( + core_state: &mut CoreState, + bool_state: &mut GlobalState, + message_raw: vector, + signature: vector, + app_cap: &AppCap, + pool_manager_info: &mut PoolManagerInfo, + user_manager_info: &mut UserManagerInfo, + ctx: &mut TxContext + ): (DolaAddress, DolaAddress, u256, vector) { + check_relayer(core_state, ctx); + + let payload_without_opcode = parse_verify_and_replay_protect( + message_raw, + signature, + get_anchor_cap(&core_state.id), + bool_state + ); + + let (pool_address, user_address, amount, app_id, _, app_payload) = + pool_codec::decode_deposit_payload(payload_without_opcode); + + // Ensure that vaa is delivered to the correct application + assert!(app_manager::get_app_id(app_cap) == app_id, EINVALID_APP); + + let (actual_amount, _) = pool_manager::add_liquidity( + pool_manager_info, + pool_address, + app_manager::get_app_id(app_cap), + (amount as u256), + ); + + if (!user_manager::is_dola_user(user_manager_info, user_address)) { + user_manager::register_dola_user_id(user_manager_info, user_address); + }; + + (pool_address, user_address, actual_amount, app_payload) + } + + /// Receive withdraw on sui network + public(friend) fun receive_withdraw( + core_state: &mut CoreState, + bool_state: &mut GlobalState, + message_raw: vector, + signature: vector, + app_cap: &AppCap, + ctx: &mut TxContext + ): (DolaAddress, vector) { + check_relayer(core_state, ctx); + + let payload_without_opcode = parse_verify_and_replay_protect( + message_raw, + signature, + get_anchor_cap(&core_state.id), + bool_state + ); + + let (user_address, app_id, _, app_payload) = + pool_codec::decode_send_message_payload(payload_without_opcode); + + // Ensure that vaa is delivered to the correct application + assert!(app_manager::get_app_id(app_cap) == app_id, EINVALID_APP); + + (user_address, app_payload) + } + + /// Send withdraw on sui network + public(friend) fun send_withdraw( + core_state: &mut CoreState, + bool_state: &mut GlobalState, + app_cap: &AppCap, + pool_manager_info: &mut PoolManagerInfo, + pool_address: DolaAddress, + user_address: DolaAddress, + source_chain_id: u16, + nonce: u64, + amount: u256, + bool_message_fee: Coin, + ctx: &mut TxContext + ): u64 { + check_relayer(core_state, ctx); + let (actual_amount, _) = pool_manager::remove_liquidity( + pool_manager_info, + pool_address, + app_manager::get_app_id(app_cap), + amount + ); + let payload = pool_codec::encode_withdraw_payload( + source_chain_id, + nonce, + pool_address, + user_address, + (actual_amount as u64) + ); + + remapping_opcode(&mut payload, client_opcode_withdraw()); + + let dst_chain_id = get_bool_chain_id( + core_state, + get_dola_chain_id(&pool_address) + ); + + send_to_bool( + dst_chain_id, + payload, + bool_message_fee, + get_anchor_cap(&core_state.id), + bool_state, + ctx + ); + + return 0 + } + + /// === Internal Functions === + + fun check_relayer(core_state: &mut CoreState, ctx: &mut TxContext) { + assert!( + dynamic_field::exists_with_type>(&mut core_state.id, Relayer {}), + ERELAYER_NOT_INIT + ); + let relayers = dynamic_field::borrow>(&mut core_state.id, Relayer {}); + assert!(vector::contains(relayers, &tx_context::sender(ctx)), ENOT_RELAYER); + } + + fun get_anchor_cap( + core_state_id: &UID + ): &AnchorCap { + let bool_anchor_cap = dynamic_object_field::borrow( + core_state_id, + BoolAnchorCap {} + ); + + return bool_anchor_cap + } + + fun send_to_bool( + dst_chain_id: u32, + payload: vector, + bool_message_fee: Coin, + anchor_cap: &AnchorCap, + global_state: &mut GlobalState, + ctx: &mut TxContext, + ) { + let remain_fee = consumer::send_message( + dst_chain_id, + messenger::pure_message(), + // bn_extra_feed not used. + std::vector::empty(), + payload, + bool_message_fee, + anchor_cap, + global_state, + ctx, + ); + + // return remaining fee + if (coin::value(&remain_fee) == 0) { + coin::destroy_zero(remain_fee); + } else { + transfer::public_transfer(remain_fee, sender(ctx)); + }; + } +} diff --git a/sui/dola_protocol/sources/bool_adapter_core/bool_adapter_verify.move b/sui/dola_protocol/sources/bool_adapter_core/bool_adapter_verify.move new file mode 100644 index 00000000..9be2ca8b --- /dev/null +++ b/sui/dola_protocol/sources/bool_adapter_core/bool_adapter_verify.move @@ -0,0 +1,122 @@ +// Copyright (c) OmniBTC, Inc. +// SPDX-License-Identifier: GPL-3.0 + +/// Verification logic module for boolnet message. Including: +/// 1) Verifying the signature +/// 2) Deduplicates incoming messages +/// 3) Verifying the reliability of the source of the message +/// 4) Check server opcode +module dola_protocol::bool_adapter_verify { + use std::vector; + + use boolamt::consumer; + use boolamt::anchor::{GlobalState, AnchorCap}; + + const EINVALID_SERVER_OPCODE: u64 = 1; + + // copy from LibBoolAdapterVerify.sol + // uint8 internal constant OFFSET = 64; + // uint8 internal constant SERVER_OPCODE_SYSTEM_BINDING = 0; + // uint8 internal constant SERVER_OPCODE_SYSTEM_UNBINDING = 1; + // + // uint8 internal constant SERVER_OPCODE_LENDING_SUPPLY = 2; + // uint8 internal constant SERVER_OPCODE_LENDING_WITHDRAW = 3; + // uint8 internal constant SERVER_OPCODE_LENDING_BORROW = 4; + // uint8 internal constant SERVER_OPCODE_LENDING_REPAY = 5; + // uint8 internal constant SERVER_OPCODE_LENDING_LIQUIDATE= 6; + // uint8 internal constant SERVER_OPCODE_LENDING_COLLATERAL= 7; + // uint8 internal constant SERVER_OPCODE_LENDING_CANCEL_COLLATERAL= 8; + // + // uint8 internal constant CLIENT_OPCODE_ADD_RELAYER = OFFSET + 0; // 64 + // uint8 internal constant CLIENT_OPCODE_REMOVE_RELAYER = OFFSET + 1; // 65 + // uint8 internal constant CLIENT_OPCODE_REGISTER_SPENDER = OFFSET + 2; // 66 + // uint8 internal constant CLIENT_OPCODE_DELETE_SPENDER = OFFSET + 3; // 67 + // uint8 internal constant CLIENT_OPCODE_WITHDRAW = OFFSET + 4; // 68 + + const OFFSET: u8 = 64; + + public fun client_opcode_add_relayer(): u8 { return OFFSET + 0 } + + public fun client_opcode_remove_relayer(): u8 { return OFFSET + 1 } + + public fun client_opcode_register_spender(): u8 { return OFFSET + 2 } + + public fun client_opcode_delete_spender(): u8 { return OFFSET + 3 } + + public fun client_opcode_withdraw(): u8 { return OFFSET + 4 } + + public fun server_opcode_system_binding(): u8 { return 0 } + + public fun server_opcode_system_unbinding(): u8 { return 1 } + + public fun server_opcode_lending_supply(): u8 { return 2 } + + public fun server_opcode_lending_withdraw(): u8 { return 3 } + + public fun server_opcode_lending_borrow(): u8 { return 4 } + + public fun server_opcode_lending_repay(): u8 { return 5 } + + public fun server_opcode_lending_liquidate(): u8 { return 6 } + + public fun server_opcode_lending_collateral(): u8 { return 7 } + + public fun server_opcode_lending_cancle_collateral(): u8 { return 8 } + + public fun remapping_opcode( + payload: &mut vector, + opcode: u8 + ) { + vector::push_back(payload, opcode) + } + + public fun check_server_opcode( + msg: &vector, + opcode: u8 + ) { + let payload_len = vector::length(msg); + let last = vector::borrow(msg, (payload_len - 1)); + + assert!( + opcode == *last, + EINVALID_SERVER_OPCODE + ) + } + + public fun is_valid_server_opcode( + opcode: u8 + ): bool { + if (opcode < server_opcode_system_binding() + || opcode > server_opcode_lending_cancle_collateral()) { + return false + }; + + return true + } + + /// Parse and verify + public fun parse_verify_and_replay_protect( + message_raw: vector, + signature: vector, + anchor_cap: &AnchorCap, + bool_state: &mut GlobalState, + ): vector { + + // All check here. + // 1) Verifies message and signatures + // 2) Deduplicates incoming messages + // 3) Verifying the reliability of the source of the message + let (payload, _) = consumer::receive_message( + message_raw, + signature, + anchor_cap, + bool_state + ); + + // 4) Check server opcode + let opcode: u8 = vector::pop_back(&mut payload); + assert!(is_valid_server_opcode(opcode), EINVALID_SERVER_OPCODE); + + return payload + } +} diff --git a/sui/dola_protocol/sources/dola_portal/lending.move b/sui/dola_protocol/sources/dola_portal/lending.move index 3ef54917..51e891ea 100644 --- a/sui/dola_protocol/sources/dola_portal/lending.move +++ b/sui/dola_protocol/sources/dola_portal/lending.move @@ -1,7 +1,8 @@ // Copyright (c) OmniBTC, Inc. // SPDX-License-Identifier: GPL-3.0 -/// Lending front-end contract portal +// Lending front-end contract portal +#[allow(unused_field, unused_function)] module dola_protocol::lending_portal { use sui::clock::Clock; use sui::coin::Coin; diff --git a/sui/dola_protocol/sources/dola_portal/lending_v2.move b/sui/dola_protocol/sources/dola_portal/lending_v2.move index 43471caf..948a2137 100644 --- a/sui/dola_protocol/sources/dola_portal/lending_v2.move +++ b/sui/dola_protocol/sources/dola_portal/lending_v2.move @@ -1,3 +1,7 @@ +// Copyright (c) OmniBTC, Inc. +// SPDX-License-Identifier: GPL-3.0 + +// Lending front-end contract portal module dola_protocol::lending_portal_v2 { use sui::clock::Clock; use sui::coin::Coin; diff --git a/sui/dola_protocol/sources/dola_portal/system.move b/sui/dola_protocol/sources/dola_portal/system.move index 306be26d..cadb2045 100644 --- a/sui/dola_protocol/sources/dola_portal/system.move +++ b/sui/dola_protocol/sources/dola_portal/system.move @@ -1,7 +1,8 @@ // Copyright (c) OmniBTC, Inc. // SPDX-License-Identifier: GPL-3.0 -/// System front-end contract portal. Including address binding, etc. +// System front-end contract portal. Including address binding, etc. +#[allow(unused_field, unused_function)] module dola_protocol::system_portal { use sui::object::{Self, UID}; use sui::transfer; diff --git a/sui/dola_protocol/sources/dola_portal/system_v2.move b/sui/dola_protocol/sources/dola_portal/system_v2.move index 256c4677..8462f4e6 100644 --- a/sui/dola_protocol/sources/dola_portal/system_v2.move +++ b/sui/dola_protocol/sources/dola_portal/system_v2.move @@ -1,3 +1,7 @@ +// Copyright (c) OmniBTC, Inc. +// SPDX-License-Identifier: GPL-3.0 + +// System front-end contract portal. Including address binding, etc. module dola_protocol::system_portal_v2 { use sui::clock::Clock; use sui::coin::Coin; diff --git a/sui/dola_protocol/sources/dola_types/dola_address.move b/sui/dola_protocol/sources/dola_types/dola_address.move index f05df411..28639f90 100644 --- a/sui/dola_protocol/sources/dola_types/dola_address.move +++ b/sui/dola_protocol/sources/dola_types/dola_address.move @@ -1,7 +1,5 @@ // Copyright (c) OmniBTC, Inc. // SPDX-License-Identifier: GPL-3.0 - -/// module dola_protocol::dola_address { use std::ascii; use std::type_name; diff --git a/sui/dola_protocol/sources/governance/genesis.move b/sui/dola_protocol/sources/governance/genesis.move index ff126cea..b8651428 100644 --- a/sui/dola_protocol/sources/governance/genesis.move +++ b/sui/dola_protocol/sources/governance/genesis.move @@ -11,6 +11,8 @@ module dola_protocol::genesis { use sui::transfer; use sui::tx_context::TxContext; + use boolamt::anchor::{register_anchor_cap, AnchorCap, GlobalState}; + friend dola_protocol::governance_v1; const E_EXIST_PACKAGE: u64 = 0; @@ -179,6 +181,22 @@ module dola_protocol::genesis { package::commit_upgrade(&mut genesis.upgrade_cap, receipt); } + /// Require upgrade_cap + public fun register_bool_anchor( + _: &GovernanceCap, + governance_genesis: &GovernanceGenesis, + committee_pk: vector, + state: &mut GlobalState, + ctx: &mut TxContext, + ): AnchorCap { + register_anchor_cap( + committee_pk, + &governance_genesis.upgrade_cap, + state, + ctx + ) + } + public fun migrate_version( _: &GovernanceCap, genesis: &mut GovernanceGenesis, diff --git a/sui/dola_protocol/sources/lending_core/bool_adapter.move b/sui/dola_protocol/sources/lending_core/bool_adapter.move new file mode 100644 index 00000000..4840df8d --- /dev/null +++ b/sui/dola_protocol/sources/lending_core/bool_adapter.move @@ -0,0 +1,571 @@ +// Copyright (c) OmniBTC, Inc. +// SPDX-License-Identifier: GPL-3.0 +module dola_protocol::lending_core_bool_adapter { + use std::vector; + + use sui::clock::Clock; + use sui::coin::Coin; + use sui::event; + use sui::sui::SUI; + use sui::tx_context::TxContext; + + use boolamt::anchor::{GlobalState, get_fee_collector}; + use boolamt::fee_collector; + use boolamt::messenger; + + use dola_protocol::dola_address::{Self, DolaAddress}; + use dola_protocol::genesis::{Self, GovernanceGenesis}; + use dola_protocol::lending_codec; + use dola_protocol::lending_core_storage::{Self as storage, Storage}; + use dola_protocol::lending_logic; + use dola_protocol::pool_codec; + use dola_protocol::oracle::PriceOracle; + use dola_protocol::pool_manager::{Self, PoolManagerInfo}; + use dola_protocol::user_manager::{Self, UserManagerInfo}; + use dola_protocol::bool_adapter_core::{Self, CoreState, get_bool_chain_id}; + use dola_protocol::bool_adapter_verify::{ + remapping_opcode, + check_server_opcode, + client_opcode_withdraw, + server_opcode_lending_supply, + server_opcode_lending_withdraw, + server_opcode_lending_borrow, + server_opcode_lending_repay, + server_opcode_lending_liquidate, + server_opcode_lending_collateral, + server_opcode_lending_cancle_collateral + }; + + + /// Errors + const ENOT_ENOUGH_LIQUIDITY: u64 = 0; + + const EINVALID_CALL_TYPE: u64 = 1; + + const ENOT_FIND_POOL: u64 = 2; + + /// Events + + struct LendingCoreEvent has drop, copy { + nonce: u64, + sender_user_id: u64, + source_chain_id: u16, + dst_chain_id: u16, + dola_pool_id: u16, + receiver: vector, + amount: u256, + liquidate_user_id: u64, + call_type: u8 + } + + /// Relay Event + struct RelayEvent has drop, copy { + // Wormhole vaa sequence + sequence: u64, + // Source chain id + source_chain_id: u16, + // Source chain transaction nonce + source_chain_nonce: u64, + // Withdraw pool + dst_pool: DolaAddress, + // Confirm that nonce is in the pool or core + call_type: u8 + } + + /// === Public Functions === + + public fun calc_withdrow_bool_message_fee( + bool_global: &mut GlobalState, + core_state: &mut CoreState, + message_raw: vector + ): u64 { + let message = messenger::message_from_bcs(&message_raw); + let payload_with_opcode = messenger::payload(&message); + let opcode = vector::pop_back(&mut payload_with_opcode); + let payload = payload_with_opcode; + + if (opcode != server_opcode_lending_withdraw() && + opcode != server_opcode_lending_borrow()){ + return 0 + }; + + let ( + user_address, + _app_id, + _, + app_payload + ) = + pool_codec::decode_send_message_payload(payload); + + let ( + source_chain_id, + nonce, + amount, + pool_address, + _receiver, + call_type + ) = + lending_codec::decode_withdraw_payload(app_payload); + + if (call_type != lending_codec::get_withdraw_type() && + call_type != lending_codec::get_borrow_type()) { + return 0 + }; + + let new_payload = pool_codec::encode_withdraw_payload( + source_chain_id, + nonce, + pool_address, + user_address, + amount + ); + remapping_opcode(&mut new_payload, client_opcode_withdraw()); + + let new_payload_length = vector::length(&new_payload); + + let dst_chain_id = get_bool_chain_id(core_state, source_chain_id); + + let fee_collector = get_fee_collector(bool_global); + let fee = fee_collector::cpt_fee( + fee_collector, + dst_chain_id, + new_payload_length, + 0 + ); + + return fee + } + + public entry fun supply( + genesis: &GovernanceGenesis, + pool_manager_info: &mut PoolManagerInfo, + user_manager_info: &mut UserManagerInfo, + bool_global: &mut GlobalState, + core_state: &mut CoreState, + oracle: &mut PriceOracle, + storage: &mut Storage, + message_raw: vector, + signature: vector, + clock: &Clock, + ctx: &mut TxContext + ) { + genesis::check_latest_version(genesis); + + // check server opcode + check_server_opcode(&message_raw, server_opcode_lending_supply()); + + let (pool, user, amount, app_payload) = bool_adapter_core::receive_deposit( + core_state, + bool_global, + message_raw, + signature, + storage::get_app_cap(storage), + pool_manager_info, + user_manager_info, + ctx + ); + let dola_pool_id = pool_manager::get_id_by_pool(pool_manager_info, pool); + let dola_user_id = user_manager::get_dola_user_id(user_manager_info, user); + lending_logic::execute_supply( + pool_manager_info, + storage, + oracle, + clock, + dola_user_id, + dola_pool_id, + amount + ); + // emit event + let (source_chain_id, nonce, receiver, call_type) = lending_codec::decode_deposit_payload(app_payload); + assert!(call_type == lending_codec::get_supply_type(), EINVALID_CALL_TYPE); + event::emit(LendingCoreEvent { + nonce, + sender_user_id: dola_user_id, + source_chain_id, + dst_chain_id: dola_address::get_dola_chain_id(&receiver), + dola_pool_id, + receiver: dola_address::get_dola_address(&receiver), + amount, + liquidate_user_id: 0, + call_type + }) + } + + public entry fun withdraw( + genesis: &GovernanceGenesis, + pool_manager_info: &mut PoolManagerInfo, + user_manager_info: &mut UserManagerInfo, + bool_global: &mut GlobalState, + core_state: &mut CoreState, + oracle: &mut PriceOracle, + storage: &mut Storage, + bool_message_fee: Coin, + message_raw: vector, + signature: vector, + clock: &Clock, + ctx: &mut TxContext + ) { + genesis::check_latest_version(genesis); + + // check server opcode + check_server_opcode(&message_raw, server_opcode_lending_withdraw()); + + let (user, app_payload) = bool_adapter_core::receive_withdraw( + core_state, + bool_global, + message_raw, + signature, + storage::get_app_cap(storage), + ctx + ); + let (source_chain_id, nonce, amount, pool, receiver, call_type) = lending_codec::decode_withdraw_payload( + app_payload + ); + assert!(call_type == lending_codec::get_withdraw_type(), EINVALID_CALL_TYPE); + let amount = (amount as u256); + let dola_pool_id = pool_manager::get_id_by_pool(pool_manager_info, pool); + let dola_user_id = user_manager::get_dola_user_id(user_manager_info, user); + let dst_pool = pool; + + // If the withdrawal exceeds the user's balance, use the maximum withdrawal + let actual_amount = lending_logic::execute_withdraw( + pool_manager_info, + storage, + oracle, + clock, + dola_user_id, + dola_pool_id, + amount, + ); + + // Check pool liquidity + let pool_liquidity = pool_manager::get_pool_liquidity(pool_manager_info, dst_pool); + assert!(pool_liquidity >= actual_amount, ENOT_ENOUGH_LIQUIDITY); + + bool_adapter_core::send_withdraw( + core_state, + bool_global, + storage::get_app_cap(storage), + pool_manager_info, + dst_pool, + receiver, + source_chain_id, + nonce, + actual_amount, + bool_message_fee, + ctx + ); + + event::emit(RelayEvent { + sequence: 0, + dst_pool, + source_chain_id, + source_chain_nonce: nonce, + call_type + }); + + event::emit(LendingCoreEvent { + nonce, + sender_user_id: dola_user_id, + source_chain_id, + dst_chain_id: dola_address::get_dola_chain_id(&receiver), + dola_pool_id, + receiver: dola_address::get_dola_address(&receiver), + amount: actual_amount, + liquidate_user_id: 0, + call_type + }) + } + + public entry fun borrow( + genesis: &GovernanceGenesis, + pool_manager_info: &mut PoolManagerInfo, + user_manager_info: &mut UserManagerInfo, + bool_global: &mut GlobalState, + core_state: &mut CoreState, + oracle: &mut PriceOracle, + storage: &mut Storage, + bool_message_fee: Coin, + message_raw: vector, + signature: vector, + clock: &Clock, + ctx: &mut TxContext + ) { + genesis::check_latest_version(genesis); + + // check server opcode + check_server_opcode(&message_raw, server_opcode_lending_borrow()); + + let (user, app_payload) = bool_adapter_core::receive_withdraw( + core_state, + bool_global, + message_raw, + signature, + storage::get_app_cap(storage), + ctx + ); + let (source_chain_id, nonce, amount, pool, receiver, call_type) = lending_codec::decode_withdraw_payload( + app_payload + ); + assert!(call_type == lending_codec::get_borrow_type(), EINVALID_CALL_TYPE); + let amount = (amount as u256); + let dola_pool_id = pool_manager::get_id_by_pool(pool_manager_info, pool); + let dola_user_id = user_manager::get_dola_user_id(user_manager_info, user); + let dst_pool = pool; + + lending_logic::execute_borrow( + pool_manager_info, + storage, + oracle, + clock, + dola_user_id, + dola_pool_id, + amount + ); + + // Check pool liquidity + let pool_liquidity = pool_manager::get_pool_liquidity(pool_manager_info, dst_pool); + assert!(pool_liquidity >= amount, ENOT_ENOUGH_LIQUIDITY); + + bool_adapter_core::send_withdraw( + core_state, + bool_global, + storage::get_app_cap(storage), + pool_manager_info, + dst_pool, + receiver, + source_chain_id, + nonce, + amount, + bool_message_fee, + ctx + ); + + event::emit(RelayEvent { + sequence: 0, + dst_pool, + source_chain_id, + source_chain_nonce: nonce, + call_type + }); + + event::emit(LendingCoreEvent { + nonce, + sender_user_id: dola_user_id, + source_chain_id, + dst_chain_id: dola_address::get_dola_chain_id(&receiver), + dola_pool_id, + receiver: dola_address::get_dola_address(&receiver), + amount, + liquidate_user_id: 0, + call_type + }) + } + + public entry fun repay( + genesis: &GovernanceGenesis, + pool_manager_info: &mut PoolManagerInfo, + user_manager_info: &mut UserManagerInfo, + bool_global: &mut GlobalState, + core_state: &mut CoreState, + oracle: &mut PriceOracle, + storage: &mut Storage, + message_raw: vector, + signature: vector, + clock: &Clock, + ctx: &mut TxContext + ) { + genesis::check_latest_version(genesis); + + // check server opcode + check_server_opcode(&message_raw, server_opcode_lending_repay()); + + let (pool, user, amount, app_payload) = bool_adapter_core::receive_deposit( + core_state, + bool_global, + message_raw, + signature, + storage::get_app_cap(storage), + pool_manager_info, + user_manager_info, + ctx + ); + let dola_pool_id = pool_manager::get_id_by_pool(pool_manager_info, pool); + let dola_user_id = user_manager::get_dola_user_id(user_manager_info, user); + lending_logic::execute_repay( + pool_manager_info, + storage, + oracle, + clock, + dola_user_id, + dola_pool_id, + amount + ); + + // emit event + let (source_chain_id, nonce, receiver, call_type) = lending_codec::decode_deposit_payload(app_payload); + assert!(call_type == lending_codec::get_repay_type(), EINVALID_CALL_TYPE); + event::emit(LendingCoreEvent { + nonce, + sender_user_id: dola_user_id, + source_chain_id, + dst_chain_id: dola_address::get_dola_chain_id(&receiver), + dola_pool_id, + receiver: dola_address::get_dola_address(&receiver), + amount, + liquidate_user_id: 0, + call_type + }) + } + + public entry fun liquidate( + genesis: &GovernanceGenesis, + pool_manager_info: &mut PoolManagerInfo, + user_manager_info: &mut UserManagerInfo, + bool_global: &mut GlobalState, + core_state: &mut CoreState, + oracle: &mut PriceOracle, + storage: &mut Storage, + message_raw: vector, + signature: vector, + clock: &Clock, + ctx: &mut TxContext + ) { + genesis::check_latest_version(genesis); + + // check server opcode + check_server_opcode(&message_raw, server_opcode_lending_liquidate()); + + let (sender, app_payload) = bool_adapter_core::receive_message( + core_state, + bool_global, + message_raw, + signature, + storage::get_app_cap(storage), + ctx + ); + let (source_chain_id, nonce, repay_pool_id, liquidate_user_id, liquidate_pool_id, call_type) = lending_codec::decode_liquidate_payload_v2( + app_payload + ); + + let liquidator = user_manager::get_dola_user_id(user_manager_info, sender); + + lending_logic::execute_liquidate( + pool_manager_info, + storage, + oracle, + clock, + liquidator, + liquidate_user_id, + liquidate_pool_id, + repay_pool_id, + ); + + event::emit(LendingCoreEvent { + nonce, + sender_user_id: liquidator, + source_chain_id, + dst_chain_id: dola_address::get_dola_chain_id(&sender), + dola_pool_id: liquidate_pool_id, + receiver: dola_address::get_dola_address(&sender), + amount: 0, + liquidate_user_id, + call_type + }) + } + + public entry fun as_collateral( + genesis: &GovernanceGenesis, + pool_manager_info: &mut PoolManagerInfo, + user_manager_info: &mut UserManagerInfo, + bool_global: &mut GlobalState, + core_state: &mut CoreState, + oracle: &mut PriceOracle, + storage: &mut Storage, + message_raw: vector, + signature: vector, + clock: &Clock, + ctx: &mut TxContext + ) { + genesis::check_latest_version(genesis); + + // check server opcode + check_server_opcode(&message_raw, server_opcode_lending_collateral()); + + // Verify that a message is valid using the wormhole + let (sender, app_payload) = bool_adapter_core::receive_message( + core_state, + bool_global, + message_raw, + signature, + storage::get_app_cap(storage), + ctx + ); + let (dola_pool_ids, call_type) = lending_codec::decode_manage_collateral_payload(app_payload); + assert!(call_type == lending_codec::get_as_colleteral_type(), EINVALID_CALL_TYPE); + let dola_user_id = user_manager::get_dola_user_id(user_manager_info, sender); + + let pool_ids_length = vector::length(&dola_pool_ids); + let i = 0; + while (i < pool_ids_length) { + let dola_pool_id = vector::borrow(&dola_pool_ids, i); + lending_logic::as_collateral( + pool_manager_info, + storage, + oracle, + clock, + dola_user_id, + *dola_pool_id + ); + i = i + 1; + }; + } + + public entry fun cancel_as_collateral( + genesis: &GovernanceGenesis, + pool_manager_info: &mut PoolManagerInfo, + user_manager_info: &mut UserManagerInfo, + bool_global: &mut GlobalState, + core_state: &mut CoreState, + oracle: &mut PriceOracle, + storage: &mut Storage, + message_raw: vector, + signature: vector, + clock: &Clock, + ctx: &mut TxContext + ) { + genesis::check_latest_version(genesis); + + // check server opcode + check_server_opcode(&message_raw, server_opcode_lending_cancle_collateral()); + + // Verify that a message is valid using the wormhole + let (sender, app_payload) = bool_adapter_core::receive_message( + core_state, + bool_global, + message_raw, + signature, + storage::get_app_cap(storage), + ctx + ); + let (dola_pool_ids, call_type) = lending_codec::decode_manage_collateral_payload(app_payload); + assert!(call_type == lending_codec::get_cancel_as_colleteral_type(), EINVALID_CALL_TYPE); + + let dola_user_id = user_manager::get_dola_user_id(user_manager_info, sender); + + let pool_ids_length = vector::length(&dola_pool_ids); + let i = 0; + while (i < pool_ids_length) { + let dola_pool_id = vector::borrow(&dola_pool_ids, i); + lending_logic::cancel_as_collateral( + pool_manager_info, + storage, + oracle, + clock, + dola_user_id, + *dola_pool_id + ); + i = i + 1; + }; + } +} diff --git a/sui/dola_protocol/sources/lending_core/boost.move b/sui/dola_protocol/sources/lending_core/boost.move index 66fc5dc0..a2c5ac61 100644 --- a/sui/dola_protocol/sources/lending_core/boost.move +++ b/sui/dola_protocol/sources/lending_core/boost.move @@ -1,3 +1,5 @@ +// Copyright (c) OmniBTC, Inc. +// SPDX-License-Identifier: GPL-3.0 module dola_protocol::boost { use std::ascii::String; use std::type_name; diff --git a/sui/dola_protocol/sources/lending_core/logic.move b/sui/dola_protocol/sources/lending_core/logic.move index d15fe50a..534cd02f 100644 --- a/sui/dola_protocol/sources/lending_core/logic.move +++ b/sui/dola_protocol/sources/lending_core/logic.move @@ -19,6 +19,7 @@ module dola_protocol::lending_logic { friend dola_protocol::lending_core_wormhole_adapter; friend dola_protocol::lending_portal; + friend dola_protocol::lending_core_bool_adapter; #[test_only] friend dola_protocol::logic_tests; diff --git a/sui/dola_protocol/sources/lending_core/storage.move b/sui/dola_protocol/sources/lending_core/storage.move index bc33b405..2e33ed13 100644 --- a/sui/dola_protocol/sources/lending_core/storage.move +++ b/sui/dola_protocol/sources/lending_core/storage.move @@ -16,6 +16,7 @@ module dola_protocol::lending_core_storage { friend dola_protocol::lending_logic; friend dola_protocol::lending_portal; friend dola_protocol::lending_core_wormhole_adapter; + friend dola_protocol::lending_core_bool_adapter; /// Errors diff --git a/sui/dola_protocol/sources/pool_manager/equilibrium_fee.move b/sui/dola_protocol/sources/pool_manager/equilibrium_fee.move index 9de646fb..c8010e75 100644 --- a/sui/dola_protocol/sources/pool_manager/equilibrium_fee.move +++ b/sui/dola_protocol/sources/pool_manager/equilibrium_fee.move @@ -1,6 +1,5 @@ // Copyright (c) OmniBTC, Inc. // SPDX-License-Identifier: GPL-3.0 - module dola_protocol::equilibrium_fee { use dola_protocol::ray_math as math; diff --git a/sui/dola_protocol/sources/pool_manager/pool_manager.move b/sui/dola_protocol/sources/pool_manager/pool_manager.move index 97c5b06c..53fe16d5 100644 --- a/sui/dola_protocol/sources/pool_manager/pool_manager.move +++ b/sui/dola_protocol/sources/pool_manager/pool_manager.move @@ -13,13 +13,15 @@ module dola_protocol::pool_manager { use sui::transfer; use sui::tx_context::TxContext; - use dola_protocol::dola_address::{Self, DolaAddress}; + use dola_protocol::dola_address::DolaAddress; use dola_protocol::equilibrium_fee; use dola_protocol::genesis::GovernanceCap; #[test_only] use dola_protocol::genesis; #[test_only] + use dola_protocol::dola_address::create_dola_address; + #[test_only] use std::ascii::string; #[test_only] use sui::test_scenario; @@ -29,6 +31,7 @@ module dola_protocol::pool_manager { friend dola_protocol::lending_portal; friend dola_protocol::wormhole_adapter_core; + friend dola_protocol::bool_adapter_core; /// Equilibrium fees are charged when liquidity is less than 60% of the target liquidity. const DEFAULT_ALPHA_1: u256 = 600000000000000000000000000; @@ -528,7 +531,7 @@ module dola_protocol::pool_manager { let pool_manager_info = test_scenario::take_shared(scenario); let dola_pool_name = string(b"USDT"); - let pool = dola_address::create_dola_address(0, b"USDT"); + let pool = create_dola_address(0, b"USDT"); let dola_pool_id = 0; register_pool_id( &governance_cap, @@ -551,7 +554,7 @@ module dola_protocol::pool_manager { public fun test_add_liquidity() { let manager = @0x22; let dola_pool_name = string(b"USDT"); - let pool = dola_address::create_dola_address(0, b"USDT"); + let pool = create_dola_address(0, b"USDT"); let amount = 100; let scenario_val = test_scenario::begin(manager); @@ -602,7 +605,7 @@ module dola_protocol::pool_manager { public fun test_remove_liquidity() { let manager = @0x22; let dola_pool_name = string(b"USDT"); - let pool = dola_address::create_dola_address(0, b"USDT"); + let pool = create_dola_address(0, b"USDT"); let amount = 100; let scenario_val = test_scenario::begin(manager); diff --git a/sui/dola_protocol/sources/ray_math/math.move b/sui/dola_protocol/sources/ray_math/math.move index 4218e6c1..018c7774 100644 --- a/sui/dola_protocol/sources/ray_math/math.move +++ b/sui/dola_protocol/sources/ray_math/math.move @@ -1,6 +1,5 @@ // Copyright (c) OmniBTC, Inc. // SPDX-License-Identifier: GPL-3.0 - module dola_protocol::ray_math { /// Used to represent 27-bit precision diff --git a/sui/dola_protocol/sources/serde/serde.move b/sui/dola_protocol/sources/serde/serde.move index 1f911e27..0aed0b01 100644 --- a/sui/dola_protocol/sources/serde/serde.move +++ b/sui/dola_protocol/sources/serde/serde.move @@ -1,6 +1,5 @@ // Copyright (c) OmniBTC, Inc. // SPDX-License-Identifier: GPL-3.0 - module dola_protocol::serde { use std::ascii; use std::bcs; diff --git a/sui/dola_protocol/sources/system_core/bool_adapter.move b/sui/dola_protocol/sources/system_core/bool_adapter.move new file mode 100644 index 00000000..7fc2d45b --- /dev/null +++ b/sui/dola_protocol/sources/system_core/bool_adapter.move @@ -0,0 +1,126 @@ +// Copyright (c) OmniBTC, Inc. +// SPDX-License-Identifier: GPL-3.0 +module dola_protocol::system_core_bool_adapter { + use sui::event; + use sui::tx_context::TxContext; + + use boolamt::anchor::GlobalState; + + use dola_protocol::dola_address; + use dola_protocol::genesis::{Self, GovernanceGenesis}; + use dola_protocol::system_codec; + use dola_protocol::system_core_storage::{Self as storage, Storage}; + use dola_protocol::user_manager::{Self, UserManagerInfo}; + use dola_protocol::bool_adapter_core::{Self, CoreState}; + use dola_protocol::bool_adapter_verify::{ + check_server_opcode, + server_opcode_system_binding, + server_opcode_system_unbinding + }; + + + /// Errors + const EINVALID_CALLTYPE: u64 = 0; + + /// Events + + struct SystemCoreEvent has copy, drop { + nonce: u64, + sender: vector, + source_chain_id: u16, + user_chain_id: u16, + user_address: vector, + call_type: u8 + } + + /// === Public Functions === + + public fun bind_user_address( + genesis: &GovernanceGenesis, + user_manager_info: &mut UserManagerInfo, + bool_state: &mut GlobalState, + core_state: &mut CoreState, + storage: &Storage, + message_raw: vector, + signature: vector, + ctx: &mut TxContext + ) { + genesis::check_latest_version(genesis); + + // check server opcode + check_server_opcode(&message_raw, server_opcode_system_binding()); + + let (sender, app_payload) = bool_adapter_core::receive_message( + core_state, + bool_state, + message_raw, + signature, + storage::get_app_cap(storage), + ctx + ); + let (source_chain_id, nonce, binded_address, call_type) = system_codec::decode_bind_payload(app_payload); + assert!(call_type == system_codec::get_binding_type(), EINVALID_CALLTYPE); + + if (sender == binded_address) { + user_manager::register_dola_user_id( + user_manager_info, + sender + ); + } else { + user_manager::bind_user_address( + user_manager_info, + sender, + binded_address + ); + }; + event::emit(SystemCoreEvent { + nonce, + sender: dola_address::get_dola_address(&sender), + source_chain_id, + user_chain_id: dola_address::get_dola_chain_id(&binded_address), + user_address: dola_address::get_dola_address(&binded_address), + call_type + }) + } + + public fun unbind_user_address( + genesis: &GovernanceGenesis, + user_manager_info: &mut UserManagerInfo, + bool_state: &mut GlobalState, + core_state: &mut CoreState, + storage: &Storage, + message_raw: vector, + signature: vector, + ctx: &mut TxContext + ) { + genesis::check_latest_version(genesis); + + // check server opcode + check_server_opcode(&message_raw, server_opcode_system_unbinding()); + + let (sender, app_payload) = bool_adapter_core::receive_message( + core_state, + bool_state, + message_raw, + signature, + storage::get_app_cap(storage), + ctx + ); + let (source_chain_id, nonce, unbinded_address, call_type) = system_codec::decode_bind_payload(app_payload); + assert!(call_type == system_codec::get_unbinding_type(), EINVALID_CALLTYPE); + + user_manager::unbind_user_address( + user_manager_info, + sender, + unbinded_address + ); + event::emit(SystemCoreEvent { + nonce, + sender: dola_address::get_dola_address(&sender), + source_chain_id, + user_chain_id: dola_address::get_dola_chain_id(&unbinded_address), + user_address: dola_address::get_dola_address(&unbinded_address), + call_type + }) + } +} diff --git a/sui/dola_protocol/sources/system_core/storage.move b/sui/dola_protocol/sources/system_core/storage.move index efc5b7f2..a5135a5c 100644 --- a/sui/dola_protocol/sources/system_core/storage.move +++ b/sui/dola_protocol/sources/system_core/storage.move @@ -1,6 +1,5 @@ // Copyright (c) OmniBTC, Inc. // SPDX-License-Identifier: GPL-3.0 - module dola_protocol::system_core_storage { use sui::object::{Self, UID}; use sui::transfer; @@ -10,6 +9,7 @@ module dola_protocol::system_core_storage { use dola_protocol::genesis::GovernanceCap; friend dola_protocol::system_core_wormhole_adapter; + friend dola_protocol::system_core_bool_adapter; struct Storage has key { id: UID, diff --git a/sui/dola_protocol/sources/system_core/system_codec.move b/sui/dola_protocol/sources/system_core/system_codec.move index c41ae870..0e790d66 100644 --- a/sui/dola_protocol/sources/system_core/system_codec.move +++ b/sui/dola_protocol/sources/system_core/system_codec.move @@ -1,6 +1,5 @@ // Copyright (c) OmniBTC, Inc. // SPDX-License-Identifier: GPL-3.0 - module dola_protocol::system_codec { use std::vector; diff --git a/sui/dola_protocol/sources/user_manager/user_manager.move b/sui/dola_protocol/sources/user_manager/user_manager.move index 8e0e901e..8ac48a3c 100644 --- a/sui/dola_protocol/sources/user_manager/user_manager.move +++ b/sui/dola_protocol/sources/user_manager/user_manager.move @@ -21,6 +21,8 @@ module dola_protocol::user_manager { friend dola_protocol::lending_portal; friend dola_protocol::system_portal; friend dola_protocol::wormhole_adapter_core; + friend dola_protocol::bool_adapter_core; + friend dola_protocol::system_core_bool_adapter; /// Errors const EALREADY_USER: u64 = 0; diff --git a/sui/dola_protocol/sources/utils/merge_coins.move b/sui/dola_protocol/sources/utils/merge_coins.move index 1cb8881d..de164be6 100644 --- a/sui/dola_protocol/sources/utils/merge_coins.move +++ b/sui/dola_protocol/sources/utils/merge_coins.move @@ -1,3 +1,5 @@ +// Copyright (c) OmniBTC, Inc. +// SPDX-License-Identifier: GPL-3.0 module dola_protocol::merge_coins { use std::vector; diff --git a/sui/dola_protocol/sources/wormhole_adapter_core/remote_gov_codec.move b/sui/dola_protocol/sources/wormhole_adapter_core/remote_gov_codec.move index 4adc5038..b3f0c02c 100644 --- a/sui/dola_protocol/sources/wormhole_adapter_core/remote_gov_codec.move +++ b/sui/dola_protocol/sources/wormhole_adapter_core/remote_gov_codec.move @@ -1,3 +1,5 @@ +// Copyright (c) OmniBTC, Inc. +// SPDX-License-Identifier: GPL-3.0 module dola_protocol::remote_gov_codec { use std::vector; diff --git a/sui/dola_protocol/sources/wormhole_adapter_core/wormhole_adapter_core.move b/sui/dola_protocol/sources/wormhole_adapter_core/wormhole_adapter_core.move index 97c0d6ec..b6dcbaca 100644 --- a/sui/dola_protocol/sources/wormhole_adapter_core/wormhole_adapter_core.move +++ b/sui/dola_protocol/sources/wormhole_adapter_core/wormhole_adapter_core.move @@ -1,9 +1,10 @@ // Copyright (c) OmniBTC, Inc. // SPDX-License-Identifier: GPL-3.0 -/// Wormhole bridge adapter, this module is responsible for adapting wormhole to pass messages for settlement center -/// applications (such as lending core). The usage of this module are: 1) Update the status of user_manager and -/// pool_manager; 2) Verify VAA and message source, decode PoolPaload, and pass it to the correct application +// Wormhole bridge adapter, this module is responsible for adapting wormhole to pass messages for settlement center +// applications (such as lending core). The usage of this module are: 1) Update the status of user_manager and +// pool_manager; 2) Verify VAA and message source, decode PoolPaload, and pass it to the correct application +#[allow(unused_field)] module dola_protocol::wormhole_adapter_core { use std::vector; diff --git a/sui/external_interfaces/Move.lock b/sui/external_interfaces/Move.lock index e0905dbf..c477c48f 100644 --- a/sui/external_interfaces/Move.lock +++ b/sui/external_interfaces/Move.lock @@ -17,6 +17,7 @@ dependencies = [ { name = "Pyth" }, { name = "Sui" }, { name = "Wormhole" }, + { name = "boolamt" }, ] [[move.package]] @@ -42,7 +43,15 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } + +dependencies = [ + { name = "Sui" }, +] + +[[move.package]] +name = "boolamt" +source = { local = "../boolamt" } dependencies = [ { name = "Sui" }, diff --git a/sui/external_interfaces/Move.toml b/sui/external_interfaces/Move.toml index 50112df3..5e10b065 100644 --- a/sui/external_interfaces/Move.toml +++ b/sui/external_interfaces/Move.toml @@ -16,7 +16,7 @@ override = true [dependencies.Wormhole] git = "https://github.com/wormhole-foundation/wormhole.git" subdir = "sui/wormhole" -rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837" +rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad" override = true [dependencies.DolaProtocol] diff --git a/sui/external_interfaces/sources/interfaces.move b/sui/external_interfaces/sources/interfaces.move index 5e2237a1..167d2fe6 100644 --- a/sui/external_interfaces/sources/interfaces.move +++ b/sui/external_interfaces/sources/interfaces.move @@ -25,8 +25,15 @@ module external_interfaces::interfaces { use dola_protocol::rates; use dola_protocol::ray_math; use dola_protocol::user_manager::{Self, UserManagerInfo}; + use dola_protocol::bool_adapter_verify::{ + server_opcode_lending_withdraw, + server_opcode_lending_borrow, + server_opcode_lending_liquidate, + server_opcode_lending_cancle_collateral, + }; use wormhole::state::State; use wormhole::vaa; + use boolamt::messenger; const HOUR: u64 = 60 * 60; @@ -942,6 +949,92 @@ module external_interfaces::interfaces { (feed_pool_ids, skip_pool_ids) } + public fun get_feed_tokens_for_relayer_bool( + pool_manager_info: &mut PoolManagerInfo, + user_manager_info: &mut UserManagerInfo, + storage: &mut Storage, + price_oracle: &mut PriceOracle, + message_raw: vector, + clock: &Clock + ): (vector, vector) { + let message = messenger::message_from_bcs(&message_raw); + let payload_with_opcode = messenger::payload(&message); + let opcode = vector::pop_back(&mut payload_with_opcode); + let payload = payload_with_opcode; + + let feed_pool_ids = vector[]; + let skip_pool_ids = vector[]; + + if (server_opcode_lending_withdraw() == opcode || server_opcode_lending_borrow() == opcode) { + let (user_address, _, _, app_payload) = + pool_codec::decode_send_message_payload(payload); + let (_, _, _, pool, _, call_type) = lending_codec::decode_withdraw_payload( + app_payload + ); + let dola_pool_id = pool_manager::get_id_by_pool(pool_manager_info, pool); + let dola_user_id = user_manager::get_dola_user_id(user_manager_info, user_address); + let collaterals = storage::get_user_collaterals(storage, dola_user_id); + let loans = storage::get_user_loans(storage, dola_user_id); + if (!vector::contains(&loans, &dola_pool_id)) { + vector::push_back(&mut feed_pool_ids, dola_pool_id); + }; + + if (vector::length(&loans) > 0 || call_type == lending_codec::get_borrow_type()) { + vector::append(&mut feed_pool_ids, collaterals); + vector::append(&mut feed_pool_ids, loans); + }; + }; + + if (server_opcode_lending_liquidate() == opcode) { + let (sender, _, _, app_payload) = + pool_codec::decode_send_message_payload(payload); + let (_, _, _, liquidate_user_id, _, _) = lending_codec::decode_liquidate_payload_v2( + app_payload + ); + let sender_dola_user_id = user_manager::get_dola_user_id(user_manager_info, sender); + let sender_collaterals = storage::get_user_collaterals(storage, sender_dola_user_id); + let sender_loans = storage::get_user_loans(storage, sender_dola_user_id); + + vector::append(&mut feed_pool_ids, sender_collaterals); + vector::append(&mut feed_pool_ids, sender_loans); + + let collaterals = storage::get_user_collaterals(storage, liquidate_user_id); + let loans = storage::get_user_loans(storage, liquidate_user_id); + + vector::append(&mut feed_pool_ids, collaterals); + vector::append(&mut feed_pool_ids, loans); + }; + + if (server_opcode_lending_cancle_collateral() == opcode) { + let (user_address, _, _, _) = + pool_codec::decode_send_message_payload(payload); + let dola_user_id = user_manager::get_dola_user_id(user_manager_info, user_address); + let collaterals = storage::get_user_collaterals(storage, dola_user_id); + let loans = storage::get_user_loans(storage, dola_user_id); + + if (vector::length(&loans) > 0) { + vector::append(&mut feed_pool_ids, collaterals); + vector::append(&mut feed_pool_ids, loans); + }; + }; + + let current_timestamp = clock::timestamp_ms(clock) / 1000; + + let usdt_pool_id = 1; + let (_, _, timestamp) = oracle::get_token_price(price_oracle, usdt_pool_id); + if (current_timestamp - timestamp < HOUR - MINUATE) { + vector::push_back(&mut skip_pool_ids, usdt_pool_id); + }; + + let usdc_pool_id = 2; + let (_, _, timestamp) = oracle::get_token_price(price_oracle, usdc_pool_id); + if (current_timestamp - timestamp < HOUR - MINUATE) { + vector::push_back(&mut skip_pool_ids, usdc_pool_id); + }; + + (feed_pool_ids, skip_pool_ids) + } + public entry fun get_feed_tokens( storage: &mut Storage, price_oracle: &mut PriceOracle, diff --git a/sui/proposals/bool_proposal/Move.lock b/sui/proposals/bool_proposal/Move.lock new file mode 100644 index 00000000..27e3dc87 --- /dev/null +++ b/sui/proposals/bool_proposal/Move.lock @@ -0,0 +1,57 @@ +# @generated by Move, please check-in and do not edit manually. + +[move] +version = 0 + +dependencies = [ + { name = "DolaProtocol" }, + { name = "Sui" }, +] + +[[move.package]] +name = "DolaProtocol" +source = { local = "../../dola_protocol" } + +dependencies = [ + { name = "Pyth" }, + { name = "Sui" }, + { name = "Wormhole" }, + { name = "boolamt" }, +] + +[[move.package]] +name = "MoveStdlib" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "09b2081498366df936abae26eea4b2d5cafb2788", subdir = "crates/sui-framework/packages/move-stdlib" } + +[[move.package]] +name = "Pyth" +source = { git = "https://github.com/pyth-network/pyth-crosschain.git", rev = "7dab308f961746890faf1ac0b52e283b31112bf6", subdir = "target_chains/sui/contracts" } + +dependencies = [ + { name = "Sui" }, + { name = "Wormhole" }, +] + +[[move.package]] +name = "Sui" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "09b2081498366df936abae26eea4b2d5cafb2788", subdir = "crates/sui-framework/packages/sui-framework" } + +dependencies = [ + { name = "MoveStdlib" }, +] + +[[move.package]] +name = "Wormhole" +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } + +dependencies = [ + { name = "Sui" }, +] + +[[move.package]] +name = "boolamt" +source = { local = "../../boolamt" } + +dependencies = [ + { name = "Sui" }, +] diff --git a/sui/proposals/bool_proposal/Move.toml b/sui/proposals/bool_proposal/Move.toml new file mode 100644 index 00000000..8795d19b --- /dev/null +++ b/sui/proposals/bool_proposal/Move.toml @@ -0,0 +1,14 @@ +[package] +name = "BoolProposal" +version = "0.0.1" + +[addresses] +bool_proposal = "0x0" + +[dependencies.Sui] +git = "https://github.com/MystenLabs/sui.git" +subdir = "crates/sui-framework/packages/sui-framework" +rev = "09b2081498366df936abae26eea4b2d5cafb2788" + +[dependencies.DolaProtocol] +local = "../../dola_protocol" diff --git a/sui/proposals/bool_proposal/sources/proposal.move b/sui/proposals/bool_proposal/sources/proposal.move new file mode 100644 index 00000000..b7926c16 --- /dev/null +++ b/sui/proposals/bool_proposal/sources/proposal.move @@ -0,0 +1,130 @@ +module bool_proposal::proposal { + use std::option; + use sui::tx_context::TxContext; + + use boolamt::anchor::{GlobalState, AnchorCap}; + + use dola_protocol::genesis::GovernanceCap; + use dola_protocol::governance_v1::{ Self, GovernanceInfo, Proposal }; + use dola_protocol::bool_adapter_core::{ + register_path, set_anchor_cap, release_anchor_cap, CoreState + }; + + + const EIS_FINAL_VOTE: u64 = 0; + + const EUNFINISHED_VOTE: u64 = 1; + + /// To prove that this is a proposal, make sure that the `certificate` in the proposal will only flow to + /// governance contract. + struct Certificate has store, drop {} + + /// Ensure that gov_cap is only used for the current contract and must be destroyed when it is finished. + struct HotPotato { + gov_cap: GovernanceCap + } + + public entry fun create_proposal( + governance_info: &mut GovernanceInfo, + ctx: &mut TxContext + ) { + governance_v1::create_proposal_with_history( + governance_info, + Certificate {}, + ctx + ) + } + + public fun vote_porposal( + governance_info: &GovernanceInfo, + proposal: &mut Proposal, + ctx: &mut TxContext + ) { + let governance_cap = governance_v1::vote_proposal( + governance_info, Certificate {}, + proposal, + true, + ctx + ); + + assert!(option::is_none(&governance_cap), EIS_FINAL_VOTE); + + option::destroy_none(governance_cap) + } + + public fun vote_proposal_final( + governance_info: &mut GovernanceInfo, + proposal: &mut Proposal, + ctx: &mut TxContext + ): HotPotato { + let governance_cap = governance_v1::vote_proposal( + governance_info, + Certificate {}, + proposal, + true, + ctx + ); + + assert!(option::is_some(&governance_cap), EUNFINISHED_VOTE); + + let gov_cap = option::extract(&mut governance_cap); + + option::destroy_none(governance_cap); + + HotPotato { gov_cap } + } + + /// Call when the proposal is complete + public fun destory(hot_potato: HotPotato) { + let HotPotato { gov_cap } = hot_potato; + governance_v1::destroy_governance_cap(gov_cap); + } + + public fun bool_register_path( + hot_potato: HotPotato, + bool_core_state: &mut CoreState, + dola_chain_id: u16, + dst_chain_id: u32, + dst_anchor: address, + bool_global_state: &mut GlobalState, + ): HotPotato { + register_path( + &hot_potato.gov_cap, + bool_core_state, + dola_chain_id, + dst_chain_id, + dst_anchor, + bool_global_state + ); + + hot_potato + } + + public fun bool_set_anchor_cap( + hot_potato: HotPotato, + core_state: &mut CoreState, + bool_anchor_cap: AnchorCap + ): HotPotato { + set_anchor_cap( + &hot_potato.gov_cap, + core_state, + bool_anchor_cap + ); + + hot_potato + } + + public fun bool_release_anchor_cap( + hot_potato: HotPotato, + core_state: &mut CoreState, + receiver: address + ): HotPotato { + release_anchor_cap( + &hot_potato.gov_cap, + core_state, + receiver + ); + + hot_potato + } +} diff --git a/sui/proposals/chain_manager_proposal/register_new_chain_20230814/Move.lock b/sui/proposals/chain_manager_proposal/register_new_chain_20230814/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/chain_manager_proposal/register_new_chain_20230814/Move.lock +++ b/sui/proposals/chain_manager_proposal/register_new_chain_20230814/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/genesis_proposal/Move.lock b/sui/proposals/genesis_proposal/Move.lock index 1757a695..b92999b4 100644 --- a/sui/proposals/genesis_proposal/Move.lock +++ b/sui/proposals/genesis_proposal/Move.lock @@ -17,6 +17,7 @@ dependencies = [ { name = "Pyth" }, { name = "Sui" }, { name = "Wormhole" }, + { name = "boolamt" }, ] [[move.package]] @@ -42,7 +43,15 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } + +dependencies = [ + { name = "Sui" }, +] + +[[move.package]] +name = "boolamt" +source = { local = "../../boolamt" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/genesis_proposal/Move.toml b/sui/proposals/genesis_proposal/Move.toml index 248f397b..41a98c24 100644 --- a/sui/proposals/genesis_proposal/Move.toml +++ b/sui/proposals/genesis_proposal/Move.toml @@ -15,7 +15,7 @@ rev = "09b2081498366df936abae26eea4b2d5cafb2788" [dependencies.Wormhole] git = "https://github.com/wormhole-foundation/wormhole.git" subdir = "sui/wormhole" -rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837" +rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad" [dependencies.DolaProtocol] local = "../../dola_protocol" diff --git a/sui/proposals/genesis_proposal/sources/genesis_proposal.move b/sui/proposals/genesis_proposal/sources/genesis_proposal.move index b4547d6d..26b48b3a 100644 --- a/sui/proposals/genesis_proposal/sources/genesis_proposal.move +++ b/sui/proposals/genesis_proposal/sources/genesis_proposal.move @@ -10,10 +10,14 @@ module genesis_proposal::genesis_proposal { use sui::coin::Coin; use sui::sui::SUI; use sui::tx_context::TxContext; + use sui::transfer; + + use wormhole::state::State; + use boolamt::anchor::{GlobalState, AnchorCap}; use dola_protocol::app_manager::TotalAppInfo; use dola_protocol::dola_address; - use dola_protocol::genesis::GovernanceCap; + use dola_protocol::genesis::{GovernanceCap, GovernanceGenesis, register_bool_anchor}; use dola_protocol::governance_v1::{Self, GovernanceInfo, Proposal}; use dola_protocol::lending_core_storage::{Self, Storage}; use dola_protocol::lending_logic; @@ -24,10 +28,9 @@ module genesis_proposal::genesis_proposal { use dola_protocol::wormhole_adapter_core::{Self, CoreState}; use dola_protocol::wormhole_adapter_pool; use dola_protocol::wormhole_adapter_pool::PoolState; - use wormhole::state::State; use dola_protocol::boost; use dola_protocol::boost::RewardPool; - use sui::transfer; + use dola_protocol::bool_adapter_core::initialize_cap_with_governance; const EIS_FINAL_VOTE: u64 = 0; @@ -120,6 +123,50 @@ module genesis_proposal::genesis_proposal { hot_potato } + // Require gov_cap + public fun init_bool_adapter_core_with_key( + hot_potato: HotPotato, + governance_genesis: &GovernanceGenesis, + committee_pk: vector, + bool_global: &mut GlobalState, + init_relayer: address, + ctx: &mut TxContext + ): HotPotato { + let bool_anchor_cap = register_bool_anchor( + &hot_potato.gov_cap, + governance_genesis, + committee_pk, + bool_global, + ctx + ); + + initialize_cap_with_governance( + &hot_potato.gov_cap, + bool_anchor_cap, + init_relayer, + ctx + ); + + hot_potato + } + + // Require gov_cap + public fun init_bool_adapter_core_with_cap( + hot_potato: HotPotato, + bool_anchor_cap: AnchorCap, + init_relayer: address, + ctx: &mut TxContext + ): HotPotato { + initialize_cap_with_governance( + &hot_potato.gov_cap, + bool_anchor_cap, + init_relayer, + ctx + ); + + hot_potato + } + public fun register_token_price( hot_potato: HotPotato, price_oracle: &mut PriceOracle, diff --git a/sui/proposals/manage_group_id/add_group_0/Move.lock b/sui/proposals/manage_group_id/add_group_0/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/manage_group_id/add_group_0/Move.lock +++ b/sui/proposals/manage_group_id/add_group_0/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/manage_member_proposal/add_member_0/Move.lock b/sui/proposals/manage_member_proposal/add_member_0/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/manage_member_proposal/add_member_0/Move.lock +++ b/sui/proposals/manage_member_proposal/add_member_0/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/manage_member_proposal/add_member_1/Move.lock b/sui/proposals/manage_member_proposal/add_member_1/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/manage_member_proposal/add_member_1/Move.lock +++ b/sui/proposals/manage_member_proposal/add_member_1/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/manage_member_proposal/add_member_2/Move.lock b/sui/proposals/manage_member_proposal/add_member_2/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/manage_member_proposal/add_member_2/Move.lock +++ b/sui/proposals/manage_member_proposal/add_member_2/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/manage_member_proposal/add_member_3/Move.lock b/sui/proposals/manage_member_proposal/add_member_3/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/manage_member_proposal/add_member_3/Move.lock +++ b/sui/proposals/manage_member_proposal/add_member_3/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/manage_member_proposal/add_member_4/Move.lock b/sui/proposals/manage_member_proposal/add_member_4/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/manage_member_proposal/add_member_4/Move.lock +++ b/sui/proposals/manage_member_proposal/add_member_4/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/manage_reward_proposal/add_reward_pool_20230812/Move.lock b/sui/proposals/manage_reward_proposal/add_reward_pool_20230812/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/manage_reward_proposal/add_reward_pool_20230812/Move.lock +++ b/sui/proposals/manage_reward_proposal/add_reward_pool_20230812/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/manage_reward_proposal/add_reward_pool_20230821/Move.lock b/sui/proposals/manage_reward_proposal/add_reward_pool_20230821/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/manage_reward_proposal/add_reward_pool_20230821/Move.lock +++ b/sui/proposals/manage_reward_proposal/add_reward_pool_20230821/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/manage_reward_proposal/migrate_reward_pool_20230816/Move.lock b/sui/proposals/manage_reward_proposal/migrate_reward_pool_20230816/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/manage_reward_proposal/migrate_reward_pool_20230816/Move.lock +++ b/sui/proposals/manage_reward_proposal/migrate_reward_pool_20230816/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/migrate_version_proposal/migrate_version_1_0_0/Move.lock b/sui/proposals/migrate_version_proposal/migrate_version_1_0_0/Move.lock index 7fbb8d45..1e029c3f 100644 --- a/sui/proposals/migrate_version_proposal/migrate_version_1_0_0/Move.lock +++ b/sui/proposals/migrate_version_proposal/migrate_version_1_0_0/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/migrate_version_proposal/migrate_version_1_0_1/Move.lock b/sui/proposals/migrate_version_proposal/migrate_version_1_0_1/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/migrate_version_proposal/migrate_version_1_0_1/Move.lock +++ b/sui/proposals/migrate_version_proposal/migrate_version_1_0_1/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/migrate_version_proposal/migrate_version_1_0_2/Move.lock b/sui/proposals/migrate_version_proposal/migrate_version_1_0_2/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/migrate_version_proposal/migrate_version_1_0_2/Move.lock +++ b/sui/proposals/migrate_version_proposal/migrate_version_1_0_2/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/migrate_version_proposal/migrate_version_1_0_3/Move.lock b/sui/proposals/migrate_version_proposal/migrate_version_1_0_3/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/migrate_version_proposal/migrate_version_1_0_3/Move.lock +++ b/sui/proposals/migrate_version_proposal/migrate_version_1_0_3/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/migrate_version_proposal/migrate_version_1_0_4/Move.lock b/sui/proposals/migrate_version_proposal/migrate_version_1_0_4/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/migrate_version_proposal/migrate_version_1_0_4/Move.lock +++ b/sui/proposals/migrate_version_proposal/migrate_version_1_0_4/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/migrate_version_proposal/migrate_version_1_0_5/Move.lock b/sui/proposals/migrate_version_proposal/migrate_version_1_0_5/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/migrate_version_proposal/migrate_version_1_0_5/Move.lock +++ b/sui/proposals/migrate_version_proposal/migrate_version_1_0_5/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/migrate_version_proposal/migrate_version_1_0_6/Move.lock b/sui/proposals/migrate_version_proposal/migrate_version_1_0_6/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/migrate_version_proposal/migrate_version_1_0_6/Move.lock +++ b/sui/proposals/migrate_version_proposal/migrate_version_1_0_6/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/migrate_version_proposal/migrate_version_1_0_7/Move.lock b/sui/proposals/migrate_version_proposal/migrate_version_1_0_7/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/migrate_version_proposal/migrate_version_1_0_7/Move.lock +++ b/sui/proposals/migrate_version_proposal/migrate_version_1_0_7/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/migrate_version_proposal/migrate_version_1_0_8/Move.lock b/sui/proposals/migrate_version_proposal/migrate_version_1_0_8/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/migrate_version_proposal/migrate_version_1_0_8/Move.lock +++ b/sui/proposals/migrate_version_proposal/migrate_version_1_0_8/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/pool_manager_proposal/register_new_pool_20230812/Move.lock b/sui/proposals/pool_manager_proposal/register_new_pool_20230812/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/pool_manager_proposal/register_new_pool_20230812/Move.lock +++ b/sui/proposals/pool_manager_proposal/register_new_pool_20230812/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/reserve_params_proposal/set_reserve_params_20230814/Move.lock b/sui/proposals/reserve_params_proposal/set_reserve_params_20230814/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/reserve_params_proposal/set_reserve_params_20230814/Move.lock +++ b/sui/proposals/reserve_params_proposal/set_reserve_params_20230814/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/restore_proposal/Move.lock b/sui/proposals/restore_proposal/Move.lock index 7fbb8d45..1e029c3f 100644 --- a/sui/proposals/restore_proposal/Move.lock +++ b/sui/proposals/restore_proposal/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/setup_governance_proposal/Move.lock b/sui/proposals/setup_governance_proposal/Move.lock index 1757a695..b720a350 100644 --- a/sui/proposals/setup_governance_proposal/Move.lock +++ b/sui/proposals/setup_governance_proposal/Move.lock @@ -42,7 +42,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/setup_governance_proposal/Move.toml b/sui/proposals/setup_governance_proposal/Move.toml index 249d7bd6..162e3d34 100644 --- a/sui/proposals/setup_governance_proposal/Move.toml +++ b/sui/proposals/setup_governance_proposal/Move.toml @@ -15,7 +15,7 @@ rev = "09b2081498366df936abae26eea4b2d5cafb2788" [dependencies.Wormhole] git = "https://github.com/wormhole-foundation/wormhole.git" subdir = "sui/wormhole" -rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837" +rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad" [dependencies.DolaProtocol] local = "../../dola_protocol" diff --git a/sui/proposals/shutdown_proposal/Move.lock b/sui/proposals/shutdown_proposal/Move.lock index 7fbb8d45..1e029c3f 100644 --- a/sui/proposals/shutdown_proposal/Move.lock +++ b/sui/proposals/shutdown_proposal/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/upgrade_proposal/upgrade_proposal_V_1_0_6/Move.lock b/sui/proposals/upgrade_proposal/upgrade_proposal_V_1_0_6/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/upgrade_proposal/upgrade_proposal_V_1_0_6/Move.lock +++ b/sui/proposals/upgrade_proposal/upgrade_proposal_V_1_0_6/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/upgrade_proposal/upgrade_proposal_V_1_0_7/Move.lock b/sui/proposals/upgrade_proposal/upgrade_proposal_V_1_0_7/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/upgrade_proposal/upgrade_proposal_V_1_0_7/Move.lock +++ b/sui/proposals/upgrade_proposal/upgrade_proposal_V_1_0_7/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/upgrade_proposal/upgrade_proposal_V_1_0_8/Move.lock b/sui/proposals/upgrade_proposal/upgrade_proposal_V_1_0_8/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/upgrade_proposal/upgrade_proposal_V_1_0_8/Move.lock +++ b/sui/proposals/upgrade_proposal/upgrade_proposal_V_1_0_8/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/proposals/upgrade_proposal/upgrade_proposal_V_1_0_9/Move.lock b/sui/proposals/upgrade_proposal/upgrade_proposal_V_1_0_9/Move.lock index a33b38b5..4ae33afd 100644 --- a/sui/proposals/upgrade_proposal/upgrade_proposal_V_1_0_9/Move.lock +++ b/sui/proposals/upgrade_proposal/upgrade_proposal_V_1_0_9/Move.lock @@ -41,7 +41,7 @@ dependencies = [ [[move.package]] name = "Wormhole" -source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "fcfe551da0f46b704b76b09ae11dca3dd9387837", subdir = "sui/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "d050ad1d67a5b7da9fb65030aad12ef5d774ccad", subdir = "sui/wormhole" } dependencies = [ { name = "Sui" }, diff --git a/sui/scripts/bool.py b/sui/scripts/bool.py new file mode 100644 index 00000000..8a636cb5 --- /dev/null +++ b/sui/scripts/bool.py @@ -0,0 +1,276 @@ +from pathlib import Path +from sui_brownie import SuiObject, SuiPackage, Argument, U16 +import dola_sui_sdk +import dola_ethereum_sdk +from dola_sui_sdk import load +from dola_sui_sdk.deploy import main as deploy_main +from dola_sui_sdk.init import batch_init +from dola_sui_sdk import sui_project +from dola_ethereum_sdk import set_ethereum_network +from dola_ethereum_sdk.deploy import deploy_bool_adapter +from dola_ethereum_sdk.init import bool_adapter_init +from dola_ethereum_sdk.load import bool_anchor_package + +dola_sui_sdk.set_dola_project_path( + Path(__file__).parent.parent.parent, + network="sui-testnet" +) +sui_project.active_account("TestAccount") + +dola_ethereum_sdk.set_dola_project_path( + Path(__file__).parent.parent.parent, +) +set_ethereum_network("bevm-test") + + +def zpad32_left(anchor: str): + return "0x" + bytes.fromhex(anchor.replace("0x", "")).rjust(32, b"\x00").hex() + + +def deploy_sui(): + deploy_main() + + +def init_sui(): + batch_init() + + +def deploy_bevm(): + deploy_bool_adapter() + + +def init_bevm(): + bool_adapter_init() + + +def get_bool_messenger(): + anchor = bool_anchor_package("0x45395fc32da85a18285639f881cc1b7785c91091") + print(anchor.messenger()) + + +def deploy_bool_proposal(): + register_path_package = SuiPackage( + package_path=Path(sui_project.project_path).joinpath("proposals/bool_proposal") + ) + register_path_package.program_publish_package(replace_address=dict( + dola_protocol=sui_project.network_config['packages']['dola_protocol']['latest'], + wormhole=sui_project.network_config['packages']['wormhole'], + pyth=sui_project.network_config['packages']['pyth'] + ), replace_publish_at=dict( + dola_protocol=sui_project.network_config['packages']['dola_protocol']['latest'], + wormhole=sui_project.network_config['packages']['wormhole'], + pyth=sui_project.network_config['packages']['pyth'], + )) + + +def proposal_type(register_path_package): + dola_protocol = sui_project.network_config['packages']['dola_protocol']['origin'] + return f"{dola_protocol}::governance_v1::Proposal<{register_path_package}" \ + f"::proposal::Certificate>" + + +def bool_register_path( + dst_dola_chainid=1502, + dst_chain_id=1502, + dst_anchor="0x45395fc32da85a18285639f881cc1b7785c91091" +): + # public fun register_path( + # _: &GovernanceCap, + # core_state: &mut CoreState, + # dola_chain_id: u16, + # dst_chain_id: u32, + # dst_anchor: address, + # bool_global_state: &mut GlobalState, + # ) + + dst_anchor = zpad32_left(dst_anchor) + print(f"dst_anchor={dst_anchor}") + + bool_proposal_package = SuiPackage( + package_id="0xa75f70081742f937eb68eaebef9cc06aff00436ecf84d1b8542dd1f232d4286f", + package_path=Path(sui_project.project_path).joinpath("proposals/bool_proposal") + ) + dola_protocol = load.dola_protocol_package() + + core_state = sui_project.network_config['bool_network']['core_state'] + bool_global = sui_project.network_config['bool_network']['global_state'] + + bool_proposal_package.proposal.create_proposal( + dola_protocol.governance_v1.GovernanceInfo[-1] + ) + + register_path_params = [ + dola_protocol.governance_v1.GovernanceInfo[-1], # 0 + sui_project[SuiObject.from_type(proposal_type(bool_proposal_package.package_id))][-1], # 1 + core_state, # 2 + dst_dola_chainid, # 3 + dst_chain_id, # 4 + dst_anchor, # 5 + bool_global # 6 + ] + + vote_proposal_final_tx_block = [ + [ + bool_proposal_package.proposal.vote_proposal_final, + [Argument("Input", U16(0)), Argument("Input", U16(1))], + [] + ] + ] + + register_path_tx_block = [ + [ + bool_proposal_package.proposal.bool_register_path, + [ + Argument("Result", U16(0)), # HotPotato + Argument("Input", U16(2)), + Argument("Input", U16(3)), + Argument("Input", U16(4)), + Argument("Input", U16(5)), + Argument("Input", U16(6)), + ], + [] + ] + ] + + finish_proposal_tx_block = [ + [ + bool_proposal_package.proposal.destory, + [ + Argument("Result", U16(1)), # HotPotato + ], + [] + ] + ] + + sui_project.batch_transaction( + actual_params=register_path_params, + transactions=vote_proposal_final_tx_block + register_path_tx_block + finish_proposal_tx_block + ) + + +def bool_release_anchor_cap(receiver): + # public fun release_anchor_cap( + # _: &GovernanceCap, + # core_state: &mut CoreState, + # receiver: address + # ) + + bool_proposal_package = SuiPackage( + package_id="0xa75f70081742f937eb68eaebef9cc06aff00436ecf84d1b8542dd1f232d4286f", + package_path=Path(sui_project.project_path).joinpath("proposals/bool_proposal") + ) + dola_protocol = load.dola_protocol_package() + + core_state = sui_project.network_config['bool_network']['core_state'] + + bool_proposal_package.proposal.create_proposal( + dola_protocol.governance_v1.GovernanceInfo[-1] + ) + + release_anchor_cap_params = [ + dola_protocol.governance_v1.GovernanceInfo[-1], # 0 + sui_project[SuiObject.from_type(proposal_type(bool_proposal_package.package_id))][-1], # 1 + core_state, # 2 + receiver, # 3 + ] + + vote_proposal_final_tx_block = [ + [ + bool_proposal_package.proposal.vote_proposal_final, + [Argument("Input", U16(0)), Argument("Input", U16(1))], + [] + ] + ] + + release_anchor_cap_tx_block = [ + [ + bool_proposal_package.proposal.bool_release_anchor_cap, + [ + Argument("Result", U16(0)), # HotPotato + Argument("Input", U16(2)), + Argument("Input", U16(3)), + ], + [] + ] + ] + + finish_proposal_tx_block = [ + [ + bool_proposal_package.proposal.destory, + [ + Argument("Result", U16(1)), # HotPotato + ], + [] + ] + ] + + sui_project.batch_transaction( + actual_params=release_anchor_cap_params, + transactions=vote_proposal_final_tx_block + release_anchor_cap_tx_block + finish_proposal_tx_block + ) + + +def bool_set_anchor_cap(bool_anchor_cap): + # public fun set_anchor_cap( + # _: &GovernanceCap, + # core_state: &mut CoreState, + # bool_anchor_cap: AnchorCap + # ) + + bool_proposal_package = SuiPackage( + package_id="0xa75f70081742f937eb68eaebef9cc06aff00436ecf84d1b8542dd1f232d4286f", + package_path=Path(sui_project.project_path).joinpath("proposals/bool_proposal") + ) + dola_protocol = load.dola_protocol_package() + + core_state = sui_project.network_config['bool_network']['core_state'] + + bool_proposal_package.proposal.create_proposal( + dola_protocol.governance_v1.GovernanceInfo[-1] + ) + + set_anchor_cap_params = [ + dola_protocol.governance_v1.GovernanceInfo[-1], # 0 + sui_project[SuiObject.from_type(proposal_type(bool_proposal_package.package_id))][-1], # 1 + core_state, # 2 + bool_anchor_cap, # 3 + ] + + vote_proposal_final_tx_block = [ + [ + bool_proposal_package.proposal.vote_proposal_final, + [Argument("Input", U16(0)), Argument("Input", U16(1))], + [] + ] + ] + + set_anchor_cap_tx_block = [ + [ + bool_proposal_package.proposal.bool_set_anchor_cap, + [ + Argument("Result", U16(0)), # HotPotato + Argument("Input", U16(2)), + Argument("Input", U16(3)), + ], + [] + ] + ] + + finish_proposal_tx_block = [ + [ + bool_proposal_package.proposal.destory, + [ + Argument("Result", U16(1)), # HotPotato + ], + [] + ] + ] + + sui_project.batch_transaction( + actual_params=set_anchor_cap_params, + transactions=vote_proposal_final_tx_block + set_anchor_cap_tx_block + finish_proposal_tx_block + ) + + +if __name__ == '__main__': + deploy_sui() diff --git a/sui/scripts/boolnetwork/__init__.py b/sui/scripts/boolnetwork/__init__.py new file mode 100644 index 00000000..847df1ca --- /dev/null +++ b/sui/scripts/boolnetwork/__init__.py @@ -0,0 +1,2 @@ +from .watcher import BoolWatcher +from .executor import BoolExecutorSui, BoolExecutorETH \ No newline at end of file diff --git a/sui/scripts/boolnetwork/db.py b/sui/scripts/boolnetwork/db.py new file mode 100644 index 00000000..af4dcdd6 --- /dev/null +++ b/sui/scripts/boolnetwork/db.py @@ -0,0 +1,78 @@ +from __future__ import annotations + +from pymongo import MongoClient, errors +import time +import datetime + + +def mongodb(mongodb_uri): + mclient = MongoClient(mongodb_uri) + return mclient["DolaProtocol"] + + +class BOOLRecord: + def __init__(self, mongodb_uri): + db = mongodb(mongodb_uri) + self.record_collection = db['BoolRecord'] + self.latest_scan = db['BoolLatestScan'] + + self._create_index() + + def _create_index(self): + self.record_collection.create_index("crossId", unique=True) + + def find_one(self, filter): + return self.record_collection.find_one(filter) + + def find(self, filter): + return self.record_collection.find(filter) + + def date(self): + current_timestamp = int(time.time()) + date = str(datetime.datetime.fromtimestamp(current_timestamp)) + + return date + +class BOOLRecordProducer(BOOLRecord): + def add_wait_record(self, message: dict): + record = { + "txUid": message["txUid"], + "crossId": message["crossId"], + "cid": message["cid"], + "verifyHash": message["verifyHash"], + "blockNum": message["blockNum"], + "blockHash": message["blockHash"], + "msg": message["msg"], + "signature": message["signature"], + 'status': 'waitForDeliver', + 'dstTx': "", + 'start_time': self.date(), + 'end_time': "", + } + + try: + self.record_collection.insert_one(record) + except errors.DuplicateKeyError: + pass + + def upsert_latest_scan(self, block_num: int): + self.latest_scan.update_one( + {"name": "latest_scan"}, + {'$set': {"name": "latest_scan", "block_num": block_num, "update": self.date()}}, + upsert=True + ) + + def get_latest_scan_num(self): + latest_scan = self.latest_scan.find_one( + {"name": "latest_scan"} + ) + + if latest_scan is None: + return 0 + else: + return latest_scan["block_num"] + + +class BOOLRecordConsumer(BOOLRecord): + def update_record(self, filter, update): + self.record_collection.update_one(filter, update) diff --git a/sui/scripts/boolnetwork/executor.py b/sui/scripts/boolnetwork/executor.py new file mode 100644 index 00000000..24166859 --- /dev/null +++ b/sui/scripts/boolnetwork/executor.py @@ -0,0 +1,148 @@ +from __future__ import annotations + +import time +import logging +from eth_abi import decode +from brownie.network.account import LocalAccount +from dola_sui_sdk import sui_project +from dola_ethereum_sdk.load import bool_messenger_package + +from .db import BOOLRecordConsumer + + +class BoolExecutorSui: + def __init__( + self, + cid, + dst_network, + dispatch, + mongodb_uri, + executor, + interval=5, + ): + self.cid = cid + self.dst_network = dst_network + self.interval = interval + + logging.info( + f"[BoolExecutorSui] init: cid={cid}, dst_network={dst_network}, executor={executor}, interval={interval}" + ) + + self.db_consumer = BOOLRecordConsumer(mongodb_uri) + + if not callable(dispatch): + raise TypeError("dispatch must be callable") + + self.dispatch = dispatch + + self.executor = executor + sui_project.active_account(self.executor) + + def run(self): + while True: + logging.debug(f"[BoolExecutorSui] waiting {self.interval} seconds...") + time.sleep(self.interval) + + try: + records = list( + self.db_consumer.find({'status': 'waitForDeliver', 'cid': self.cid}).sort("blockNum", 1).limit(1) + ) + + for record in records: + try: + # lendingBool.dispatch + result = self.dispatch(record["msg"], record["signature"]) + + if result["effects"]["status"]["status"] == "success": + status = "Delivered" + dstTx = result['effects']['transactionDigest'] + else: + status = "Failed" + dstTx = "" + except Exception as e: + logging.warning(f"[BoolExecutorSui] Deliver record failed: {record['crossId']}, err={e}") + status = "Failed" + dstTx = "" + + self.db_consumer.update_record( + {'status': 'waitForDeliver', 'crossId': record["crossId"]}, + {'$set': {'dstTx': dstTx, 'status': status, 'end_time': self.db_consumer.date()}} + ) + + logging.info(f"[BoolExecutorSui] Update record: crossId={record['crossId']}, status={status}") + except Exception as e: + logging.warning(f"[BoolExecutorSui] Find records failed: {e}") + continue + + + +class BoolExecutorETH: + MessageABI = ["bytes32", "bytes32", "bytes32", "bytes", "bytes32", "bytes"] + + def __init__( + self, + cid, + dst_network, + messenger, + mongodb_uri, + executor, + interval=5 + ): + self.cid = cid + self.dst_network = dst_network + self.interval = interval + + logging.info( + f"[BoolExecutorETH] init: cid={cid}, dst_network={dst_network}, executor={executor}, interval={interval}" + ) + + if not isinstance(executor, LocalAccount): + raise TypeError("executor must be brownie.network.account.LocalAccount") + + self.executor = executor + self.db_consumer = BOOLRecordConsumer(mongodb_uri) + self.messenger = bool_messenger_package(messenger) + + def run(self): + while True: + logging.debug(f"[BoolExecutorETH] waiting {self.interval} seconds...") + time.sleep(self.interval) + + try: + records = list( + self.db_consumer.find({'status': 'waitForDeliver', 'cid': self.cid}).sort("blockNum", 1).limit(1) + ) + + for record in records: + # function receiveFromBool( + # Message memory message, + # bytes calldata signature + # ) external override nonReentrant + try: + msg = bytes.fromhex(record["msg"].replace("0x", "")) + signature = bytes.fromhex(record["signature"].replace("0x", "")) + + receipt = self.messenger.receiveFromBool( + decode(self.MessageABI, msg), + signature, + {"from": self.executor, "gas_price": self.gas_price} + ) + status = "Delivered" + dstTx = receipt.txid + + except Exception as e: + logging.warning(f"[BoolExecutorETH] Deliver record failed: {record['crossId']}, err={e}") + status = "Failed" + dstTx = "" + + self.db_consumer.update_record( + {'status': 'waitForDeliver', 'crossId': record["crossId"]}, + {'$set': {'dstTx': dstTx, 'status': status, 'end_time': self.db_consumer.date()}} + ) + + logging.info(f"[BoolExecutorETH] Update record: crossId={record['crossId']}, status={status}") + except Exception as e: + logging.warning(f"[BoolExecutorETH] Find records failed: {e}") + continue + + diff --git a/sui/scripts/boolnetwork/message.py b/sui/scripts/boolnetwork/message.py new file mode 100644 index 00000000..7fe808ed --- /dev/null +++ b/sui/scripts/boolnetwork/message.py @@ -0,0 +1,396 @@ +from __future__ import annotations + +import json +import logging +from typing import Union +import eth_abi +from eth_abi.encoding import encode_uint_256 +from eth_abi.registry import BaseEquals, encoding +from eth_abi.utils.padding import zpad_right + + +class NewByteStringEncoder(encoding.ByteStringEncoder): + is_dynamic = True + + @classmethod + def encode(cls, value): + cls.validate_value(value) + value_length = len(value) + + encoded_size = encode_uint_256(value_length) + + if value_length == 0: + return encoded_size + + ceil32 = ( + value_length + if value_length % 32 == 0 + else value_length + 32 - (value_length % 32) + ) + padded_value = zpad_right(value, ceil32) + + return encoded_size + padded_value + + +eth_abi.abi.registry.unregister_encoder( + BaseEquals("bytes", with_sub=False), +) + +eth_abi.abi.registry.register_encoder( + BaseEquals("bytes", with_sub=False), NewByteStringEncoder +) + + +def convert_to_cross_id(tx_uid: Union[str, bytes]): + """ + :param tx_uid: "0x000001440000a4b1000000000000000000000000000000000000000000000036" + :return: "324:42161:54" + """ + if type(tx_uid) == str: + tx_uid = tx_uid[2:] if tx_uid.startswith("0x") else tx_uid + bytes32 = bytes.fromhex(tx_uid) + else: + assert len(tx_uid) == 32, tx_uid + bytes32 = tx_uid + + source_chain = int.from_bytes(bytes32[:4], "big") + target_chain = int.from_bytes(bytes32[4:8], "big") + nonce = int.from_bytes(bytes32[8:], "big") + + return f"{source_chain}:{target_chain}:{nonce}" + + +def convert_to_uid(uid: str): + """ + :param uid: "324:42161:54" + :return: "0x000001440000a4b1000000000000000000000000000000000000000000000036" + """ + tokens = uid.split(":") + assert len(tokens) == 3, tokens + + source_chain = int(tokens[0]) + target_chain = int(tokens[1]) + nonce = int(tokens[2]) + + uid = source_chain << 32 + uid += target_chain + uid = uid << 192 + uid += nonce + + uid_bytes = uid.to_bytes(length=32, byteorder="big", signed=False) + + return f"0x{uid_bytes.hex()}" + + +class Message: + # On EVM + # struct Message { + # bytes32 txUniqueIdentification; + # bytes32 crossType; + # bytes32 srcAnchor; + # bytes bnExtraFeed; + # bytes32 dstAnchor; + # bytes payload; + # } + # On Sui + # struct Message has copy, drop { + # tx_unique_identification: u256, + # cross_type: vector, + # src_anchor: address, + # dst_anchor: address, + # bn_extra_feed: vector, + # payload: vector, + # } + + MessageABI = ["bytes32", "bytes32", "bytes32", "bytes", "bytes32", "bytes"] + + def __init__(self, msg: bytes, to_sui: bool): + if to_sui: + self._decode_bcs(msg) + self.to_sui = True + self.origin = msg + else: + self._decode_rlp(msg[32:]) + self.to_sui = False + self.origin = msg[32:] + + def __str__(self): + return json.dumps(self.format_json(), indent=2) + + def __repr__(self): + return self.__str__() + + def encode(self): + if self.to_sui: + return self._encode_bcs() + else: + return self._encode_rlp() + + def _decode_rlp(self, msg: bytes): + ( + self.txUniqueIdentification, + self.crossType, + self.srcAnchor, + self.bnExtraFeed, + self.dstAnchor, + self.payload, + ) = eth_abi.decode(self.MessageABI, msg) + + def _encode_rlp(self): + output = eth_abi.encode( + self.MessageABI, + ( + self.txUniqueIdentification, + self.crossType, + self.srcAnchor, + self.bnExtraFeed, + self.dstAnchor, + self.payload, + ), + ) + + assert self.origin == output, str(self) + + return output + + @classmethod + def _decode_u32_from_uleb128(cls, data: bytearray): + value = 0 + index = 0 + + for shift in range(0, 32, 7): + byte = data[index] + value |= (byte & 0x7F) << shift + index += 1 + + if not (byte & 0x80): + break + + return value, index + + @classmethod + def _encode_u32_to_uleb128(cls, value: int): + output = bytearray() + while value >= 0x80: + # Write 7 (lowest) bits of data and set the 8th bit to 1. + byte = value & 0x7F + output.append(byte | 0x80) + value >>= 7 + + # Write the remaining bits of data and set the highest bit to 0. + output.append(value & 0x7F) + + return output + + @classmethod + def _decode_bytes(cls, data: bytearray): + length, index = cls._decode_u32_from_uleb128(data) + + # print(length, index, len(data[index:]), f"{list(data[index:])}") + + assert length <= len(data[index:]), length + + return data[index : index + length], index + length + + @classmethod + def _encode_bytes(cls, data: bytes): + output = bytearray() + + output += cls._encode_u32_to_uleb128(len(data)) + output += data + + return output + + def _decode_bcs(self, msg: bytes): + # struct Message has copy, drop { + # tx_unique_identification: u256, + # cross_type: vector, + # src_anchor: address, + # dst_anchor: address, + # bn_extra_feed: vector, + # payload: vector, + # } + + bytes_array = bytearray(msg) + next_index = 0 + + self.txUniqueIdentification = bytes_array[:32] + # evm big endian => sui little endian + self.txUniqueIdentification.reverse() + + next_index += 32 + + self.crossType, length = Message._decode_bytes(bytes_array[next_index:]) + next_index += length + + self.srcAnchor = bytes_array[next_index : next_index + 32] + next_index += 32 + + self.dstAnchor = bytes_array[next_index : next_index + 32] + next_index += 32 + + self.bnExtraFeed, length = Message._decode_bytes(bytes_array[next_index:]) + next_index += length + + self.payload, length = Message._decode_bytes(bytes_array[next_index:]) + next_index += length + + assert next_index == len(msg), next_index + + def _encode_bcs(self): + # struct Message has copy, drop { + # tx_unique_identification: u256, + # cross_type: vector, + # src_anchor: address, + # dst_anchor: address, + # bn_extra_feed: vector, + # payload: vector, + # } + output = bytearray() + + output += self.txUniqueIdentification + output.reverse() + + output += Message._encode_bytes(self.crossType) + + output += self.srcAnchor + + output += self.dstAnchor + + output += Message._encode_bytes(self.bnExtraFeed) + + output += Message._encode_bytes(self.payload) + + assert self.origin == output, str(self) + + return output + + def format_json(self): + return { + "to_sui": self.to_sui, + "txUniqueIdentification": "0x" + bytes(self.txUniqueIdentification).hex(), + "crossType": "0x" + bytes(self.crossType).hex(), + "srcAnchor": "0x" + bytes(self.srcAnchor).hex(), + "bnExtraFeed": "0x" + bytes(self.bnExtraFeed).hex(), + "dstAnchor": "0x" + bytes(self.dstAnchor).hex(), + "payload": "0x" + bytes(self.payload).hex(), + "origin": "0x" + bytes(self.origin).hex(), + } + + def tx_uid(self): + return "0x" + bytes(self.txUniqueIdentification).hex() + + def cross_id(self): + return convert_to_cross_id(self.txUniqueIdentification) + + +class EventSubmitTransaction: + def __init__(self, block_num, block_hash, event): + self.verify_hash = None + self.cid = None + self.block_num = block_num + self.block_hash = block_hash + self.origin_event = event + + self.get_cid_hash(event) + + def __str__(self): + return json.dumps(self.format_json()) + + def __repr__(self): + return json.dumps(self.format_json()) + + def format_json(self): + return { + "blockNum": self.block_num, + "blockHash": self.block_hash, + "event": self.origin_event, + } + + def get_cid_hash(self, event): + format_str = ( + format(f"{event}") + .replace("'", '"') + .replace("(", "[") + .replace(")", "]") + .replace("None", '""') + ) + + format_json = json.loads(format_str) + + cid = format_json["event"]["attributes"][0] + if type(cid) is dict: + cid = cid["value"] + + verify_hash = format_json["event"]["attributes"][3] + if type(verify_hash) is dict: + verify_hash = verify_hash["value"] + + self.cid = int(cid) + self.verify_hash = verify_hash + + +class TxMessage: + def __init__(self, event: EventSubmitTransaction, verify: bool = False): + self.event = event + self.msg = None + self.decoded = None + self.signature = None + self.verify = verify + + def __str__(self): + return json.dumps(self.format_json(), indent=2) + + def format_json(self): + return { + "txUid": self.decoded.tx_uid(), + "crossId": self.decoded.cross_id(), + "cid": self.event.cid, + "verifyHash": self.event.verify_hash, + "blockNum": self.event.block_num, + "blockHash": self.event.block_hash, + "msg": "0x" + self.msg.hex(), + "signature": "0x" + self.signature.hex(), + } + + def check_msg(self, msg: bytes, to_sui: bool): + try: + self.decoded = Message(msg, to_sui) + encode = self.decoded.encode() + except Exception as e: + logging.error(f"[BoolTxMessage] check_msg: msg={msg.hex()}, to_sui={to_sui}") + + raise e + + return encode + + def parse_storage_obj(self, storage_obj, to_sui: bool = False): + msg = bytes(storage_obj["msg"]) + signature = bytearray(storage_obj["signature"]) + + if not to_sui: + signature[64] += 27 + + checked_msg = self.check_msg(msg, to_sui) + + self.msg = checked_msg + self.signature = bytes(signature) + + +def test_message(): + # { + # "to_sui": true, + # "txUniqueIdentification": "0x00066eed7257a51b00000000000000000000000000000000000000000000006d", + # "crossType": "0x966c63d14939ec9ace2dc744f5ea970e1cc6f20f12afefdcdff58ed5d321637e", + # "srcAnchor": "0x0000000000000000000000005700d38903da2d71fa2a912a3790c80b4a03e77a", + # "bnExtraFeed": "0x", + # "dstAnchor": "0x4a9158f9c54e568512d82c7756ed6a53faef0d06c9b63e08f0a79983b3fafdc9", + # "payload": "0x676f6f64", + # "origin": "0x6d00000000000000000000000000000000000000000000001ba55772ed6e060020966c63d14939ec9ace2dc744f5ea970e1cc6f20f12afefdcdff58ed5d321637e0000000000000000000000005700d38903da2d71fa2a912a3790c80b4a03e77a4a9158f9c54e568512d82c7756ed6a53faef0d06c9b63e08f0a79983b3fafdc90004676f6f64" + # } + msg = bytes.fromhex( + "6d00000000000000000000000000000000000000000000001ba55772ed6e060020966c63d14939ec9ace2dc744f5ea970e1cc6f20f12afefdcdff58ed5d321637e0000000000000000000000005700d38903da2d71fa2a912a3790c80b4a03e77a4a9158f9c54e568512d82c7756ed6a53faef0d06c9b63e08f0a79983b3fafdc90004676f6f64" + ) + + decode_msg = Message(msg, True) + assert decode_msg.encode() == msg diff --git a/sui/scripts/boolnetwork/type.py b/sui/scripts/boolnetwork/type.py new file mode 100644 index 00000000..aec1fab2 --- /dev/null +++ b/sui/scripts/boolnetwork/type.py @@ -0,0 +1,246 @@ +BoolTypes = { + # "runtime_id": 167, + "types": { + "CommitteeId": "u32", + "SubmitTransaction": { + "type": "struct", + "type_mapping": [ + ["cid", "CommitteeId"], + ["u32", "u32"], + ["u8", "u8"], + ["Hash", "H256"], + ], + }, + "AuthorityId": "[u8; 32]", + "Address": "MultiAddress", + "AccountId": "H160", + "ExtrinsicSignature": "EthereumSignature", + "LookupSource": "MultiAddress", + "DIdentity": { + "type": "struct", + "type_mapping": [["version", "u16"], ["pk", "Vec"]], + }, + "Device": { + "type": "struct", + "type_mapping": [ + ["owner", "AccountId"], + ["did", "DIdentity"], + ["report", "Vec"], + ["state", "DeviceState"], + ], + }, + "DeviceState": { + "type": "enum", + "value_list": [ + "Unmount", + "Stop", + "Standby", + "Offline", + "Serving", + "TryExit", + ], + }, + "StakingState": { + "type": "struct", + "type_mapping": [ + ["user", "AccountId"], + ["locked", "Balance"], + ["start_time", "u64"], + ], + }, + "OnChainEvent": { + "type": "enum", + "type_mapping": [["OnChainPayload", "OnChainPayload"]], + }, + "OnChainPayload": { + "type": "struct", + "type_mapping": [ + ["did", "DIdentity"], + ["proof", "Vec"], + ["timestamp", "u64"], + ["session", "BlockNumber"], + ["signature", "Vec"], + ["enclave", "Vec"], + ], + }, + "ProviderId": "u32", + "ProviderInfo": { + "type": "struct", + "type_mapping": [ + ["pid", "ProviderId"], + ["owner", "AccountId"], + ["devices", "Vec"], + ["cap_pledge", "Balance"], + ["total_pledge", "Balance"], + ["score", "u128"], + ["rewards", "Balance"], + ["punishment", "Balance"], + ["staking_user_num", "u8"], + ["status", "ProviderState"], + ], + }, + "ProviderState": {"type": "enum", "value_list": ["Stop", "Working"]}, + "StakeInfo": { + "type": "struct", + "type_mapping": [ + ["locked", "Balance"], + ["available_rewards", "Vec<(ProviderId, DIdentity, Balance)>"], + ], + }, + "MessageOrigin": { + "type": "enum", + "type_mapping": [ + ["Pallet", "Vec"], + ["Did", "DIdentity"], + ["AccountId", "AccountId"], + ], + }, + "Message": { + "type": "struct", + "type_mapping": [ + ["sender", "MessageOrigin"], + ["destination", "Vec"], + ["payload", "Vec"], + ], + }, + "CommitteeState": { + "type": "enum", + "value_list": [ + "Creating", + "Initializing", + "Stop", + "Working", + "CreateFinished", + ], + }, + "ChannelState": {"type": "enum", "value_list": ["Stop", "Working"]}, + "HandleConnection": { + "type": "enum", + "type_mapping": [ + ["Cid", "CommitteeId"], + ["CidWithAnchor", "(CommitteeId, u32, Vec)"], + ["CommitteeParam", "(u16, u16, CryptoType, u8)"], + ], + }, + "CryptoType": { + "type": "enum", + "value_list": ["Ecdsa", "Bls", "Schnorr", "Eddsa"], + }, + "Source": {"type": "enum", "value_list": ["Mining", "Serving"]}, + "Channel": { + "type": "struct", + "type_mapping": [ + ["channel_id", "u32"], + ["creator", "AccountId"], + ["info", "Vec"], + ["cids", "Vec<(CommitteeId, u32)>"], + ["state", "ChannelState"], + ], + }, + "Parameters": {"type": "struct", "type_mapping": [["t", "u16"], ["n", "u16"]]}, + "ExitParameters": { + "type": "enum", + "type_mapping": [ + ["Normal", "Vec"], + ["Force", "(CommitteeId, u8, Vec, Vec)"], + ], + }, + "OnChainPayloadVRF": { + "type": "struct", + "type_mapping": [ + ["cid", "CommitteeId"], + ["epoch", "u32"], + ["pk", "Vec"], + ["proof", "Vec"], + ["fork_id", "u8"], + ], + }, + "Committee": { + "type": "struct", + "type_mapping": [ + ["cid", "CommitteeId"], + ["creator", "AccountId"], + ["epoch", "u32"], + ["parameters", "Parameters"], + ["pubkey", "Vec"], + ["state", "CommitteeState"], + ["crypto", "CryptoType"], + ["fork", "u8"], + ["channel_id", "u32"], + ["chain_id", "u32"], + ["anchor", "Vec"], + ["times", "(BlockNumber, BlockNumber)"], + ], + }, + "TxSource": { + "type": "struct", + "type_mapping": [ + ["chain_type", "u16"], + ["uid", "Vec"], + ["from", "Vec"], + ["to", "Vec"], + ["amount", "U256"], + ], + }, + "BlockNumber": "u32", + "TxMessage": { + "type": "struct", + "type_mapping": [ + ["cid", "u32"], + ["epoch", "u32"], + ["sid", "u64"], + ["msg", "Vec"], + ["txsource", "TxSource"], + ["signature", "Vec"], + ["time_limit", "BlockNumber"], + ["choose_index", "Vec"], + ["status", "TxStatus"], + ], + }, + "EpochChange": { + "type": "struct", + "type_mapping": [ + ["msg", "Vec"], + ["signature", "Vec"], + ["pubkey", "Vec"], + ], + }, + "TxStatus": { + "type": "enum", + "value_list": ["Unsigned", "Finished", "Abnormal", "Drop"], + }, + "BtcTxTunnel": {"type": "enum", "value_list": ["Empty", "Verifying", "Open"]}, + "ConfirmType": {"type": "enum", "value_list": ["SourceHash", "TxData"]}, + "DstChainType": { + "type": "enum", + "value_list": ["Raw", "Fil", "Bsc", "BtcMainnet", "Eth", "BtcTestnet"], + }, + "SourceTXInfo": { + "type": "struct", + "type_mapping": [["src_chain_id", "u32"], ["src_hash", "Vec"]], + }, + "RandomNumberParams": { + "type": "struct", + "type_mapping": [ + ["consumer_addr", "Vec"], + ["bls_cid_and_sig", "(u32, Vec)"], + ["ecdsa_cid_and_sig", "(u32, Vec)"], + ["number", "Vec"], + ], + }, + }, + "versioning": [], +} + +ChainType = { + 0: "Raw", + 1: "Fil", + 2: "Bsc", + 3: "Btc", + 4: "Eth", + 5: "Solana", + 6: "Aptos", + 7: "Starknet", + 8: "Sui", + 9: "Substrate", +} \ No newline at end of file diff --git a/sui/scripts/boolnetwork/verify.py b/sui/scripts/boolnetwork/verify.py new file mode 100644 index 00000000..6ca2ba96 --- /dev/null +++ b/sui/scripts/boolnetwork/verify.py @@ -0,0 +1,152 @@ +from __future__ import annotations +import logging +from nacl.signing import VerifyKey +from eth_utils import keccak +from eth_keys.datatypes import PublicKey, Signature +from typing import Union + + +def keccak_hash(msg: bytes): + return keccak(msg) + + +class ED25519PublicKey: + LENGTH: int = 32 + + key: VerifyKey + + def __init__(self, key: Union[bytes, str]): + if isinstance(key, bytes): + self.key = VerifyKey(key) + else: + key_bytes = bytes.fromhex(key.replace("0x", "")) + self.key = VerifyKey(key_bytes) + + def __str__(self) -> str: + return f"0x{self.key.encode().hex()}" + + def hex(self): + return str(self) + + def verify(self, data: bytes, signature: bytes) -> bool: + try: + self.key.verify(keccak_hash(data), signature) + except Exception as e: + logging.warning(f"[ED25519PublicKey] verify failed: {e}") + return False + return True + + +class ECDSAPublicKey: + LENGTH: int = 65 + PREFIX: str = "0x04" + + key: PublicKey + + def __init__(self, key: Union[bytes, str]): + if isinstance(key, bytes): + self.key = PublicKey(key) + else: + key_bytes = bytes.fromhex(key.replace(self.PREFIX, "").replace("0x", "")) + self.key = PublicKey(key_bytes) + + def __str__(self) -> str: + return f"0x{self.key.to_compressed_bytes().hex()}" + + def hex(self): + return str(self) + + def verify(self, data: bytes, signature: bytes) -> bool: + assert signature[64] in [27, 28], "Invalid eth signature" + + try: + _signature = bytearray(signature) + _signature[64] -= 27 + + _data = ethereum_signable_message(keccak_hash(data)) + + self.key.verify_msg_hash(_data, Signature(_signature)) + except Exception as e: + logging.warning(f"[ECDSAPublicKey] verify failed: {e}") + return False + return True + + +def ethereum_signable_message(msg: bytes): + buffer = b"\x19Ethereum Signed Message:\n" + msg_len = str(len(msg)).encode() + + buffer += msg_len + buffer += msg + + return keccak_hash(buffer) + + +def test_ethereum_signable_message(): + msg = "123".encode() + expect_hash = bytes.fromhex( + "3b453794f074c43f21713fe98eaccb2728a71bd4584e5d5958e7e73546e02603" + ) + + assert expect_hash == ethereum_signable_message(msg) + + +def test_ecdsa_verify(): + # BoolWatcher( + # testnet_url, + # begin_height=6843506, + # max_block_range=20, + # filter_cids=[302, 303, 304], + # sui_cids=[302] + # ).subscribe() + msg = bytes.fromhex( + "7257a51b000001a4000000000000000000000000000000000000000000000002966c63d14939ec9ace2dc744f5ea970e1cc6f20f12afefdcdff58ed5d321637e4a9158f9c54e568512d82c7756ed6a53faef0d06c9b63e08f0a79983b3fafdc900000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000993e87af195ac2ab570154f101ffe6463023dcb200000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000" + ) + signature = bytes.fromhex( + "e0394229e79478dead3173459421dd690532d204c9747b882f1ff3612a77422a314d8724a16e25ba1b3c9989f13ffc9275b72bcd99047c481da1aabeaad515901b" + ) + + origin_key = "0x040214a5530d403a804b8bafafa84468716c5167038466330ea36a386ba27a9f611339e2c465d159c30b3777b356ee6abfa35d2b2856e4f75c7dd0d1ae575d8c21" + + pubkey = ECDSAPublicKey(origin_key) + + assert pubkey.verify(msg, signature) + + +def test_ed25519_verify(): + # BoolWatcher( + # testnet_url, + # begin_height=6414941, + # max_block_range=20, + # filter_cids=[302, 303, 304], + # sui_cids=[302] + # ).subscribe() + msg = bytes.fromhex( + "6d00000000000000000000000000000000000000000000001ba55772ed6e060020966c63d14939ec9ace2dc744f5ea970e1cc6f20f12afefdcdff58ed5d321637e0000000000000000000000005700d38903da2d71fa2a912a3790c80b4a03e77a4a9158f9c54e568512d82c7756ed6a53faef0d06c9b63e08f0a79983b3fafdc90004676f6f64" + ) + signature = bytes.fromhex( + "07dca118b7b44dd142f6be41ce4bfc626175516bc833dcc0f99e4330de20178f5993b8611bbf1c2f4e043f3b28f22ee3824277f98a54c26713d5c2707834a703" + ) + pubkey = ED25519PublicKey( + "0x2d8d58b9cbbdcfaa8a730722b7aa22e7dc69c24c86f2cf2bd62c1f6a33d8db7f" + ) + + assert pubkey.verify(msg, signature) + + msg = bytes.fromhex( + "6800000000000000000000000000000000000000000000001ba55772de05000020966c63d14939ec9ace2dc744f5ea970e1cc6f20f12afefdcdff58ed5d321637e000000000000000000000000b6b2e33305af7335b936decf0c5b6c53f24892cbbe8b88a09f3f14ebd6474cad34c75d75eebd89a253ec768ccb1f047fb980b485000568656c6c6f" + ) + signature = bytes.fromhex( + "b2e63ea16acfc1a1b4d5669aeeddc4e54b38e911c590816af2b05d2306d5f634bff5e767520848feb0d318e0404f386680621471ad2fc8768c30ad2c4b954909" + ) + pubkey = ED25519PublicKey( + "0x90671c9eb7ad7b1e62384a177e0d5cab1f53089d87bfb783bed543d89069e5f0" + ) + + assert pubkey.verify(msg, signature) + + +if __name__ == "__main__": + test_ethereum_signable_message() + test_ecdsa_verify() + test_ed25519_verify() diff --git a/sui/scripts/boolnetwork/watcher.py b/sui/scripts/boolnetwork/watcher.py new file mode 100644 index 00000000..9307a139 --- /dev/null +++ b/sui/scripts/boolnetwork/watcher.py @@ -0,0 +1,222 @@ +from __future__ import annotations + +import logging +import time + +from substrateinterface import SubstrateInterface +from .message import TxMessage, EventSubmitTransaction +from .db import BOOLRecordProducer +from .verify import ECDSAPublicKey, ED25519PublicKey +from .type import BoolTypes + + +def subscription_handler(obj, update_nr, subscription_id): + block = obj["header"]["number"] + + logging.debug( + f"[BoolWatcher] subscription_handler: New block #{block}, #{update_nr}, @{subscription_id}" + ) + + if update_nr > 10: + return { + "message": "Subscription will cancel when a value is returned", + "updates_processed": update_nr, + } + + return block + + +class BoolWatcher: + def __init__( + self, + ws_url, + mongodb_uri, + config: dict = None, + begin_num: int = 0, + max_block_range: int = 100, + wait_seconds: int = 10, + verify: bool = False, + ): + self.url = ws_url + self.provider = SubstrateInterface( + url=ws_url, type_registry=BoolTypes, type_registry_preset="legacy" + ) + self.max_block_range = max_block_range + + self.latest = self._latest_number() + self.wait_seconds = wait_seconds + self.verify = verify + self.db = BOOLRecordProducer(mongodb_uri=mongodb_uri) + + latest_scan = self.db.get_latest_scan_num() + + self.begin_num = latest_scan if begin_num < latest_scan else begin_num + + self.filter_cids = [] + self.sui_cids = [] + self.public_keys = {} + for cid, key_info in config.items(): + self.filter_cids.append(cid) + if key_info["key_type"] == "eddsa": + self.sui_cids.append(cid) + self.public_keys[cid] = ED25519PublicKey(key_info["committee_key"]) + else: + self.public_keys[cid] = ECDSAPublicKey(key_info["committee_key"]) + + logging.info( + f"[BoolWatcher] init: begin_num={self.begin_num}, " + f"latest_scan={latest_scan}, " + f"latest_num={self.latest}, " + f"max_block_range={self.max_block_range}, " + f"filter_cids={self.filter_cids}, " + f"sui_cids={self.sui_cids}" + ) + + def _latest_number(self): + latest_hash = self.provider.get_chain_finalised_head() + return self.provider.get_block_number(latest_hash) + + def _filter_events( + self, + pallet_name="Channel", + event_name="SubmitTransaction", + block_start: int = None, + block_end: int = None, + ): + if block_end is None: + block_end = self.provider.get_block_number("") + + if block_start is None: + block_start = block_end + + if block_start < 0: + block_start += block_end + + # Requirements check + if block_end - block_start > self.max_block_range: + logging.error( + f"[BoolWatcher] max_block_range: ({self.max_block_range}) exceeded" + ) + raise ValueError("BoolWatcher _filter_events") + + result = [] + + logging.info( + f"[BoolWatcher] _filter_events: begin [#{block_start}, #{block_end}]" + ) + + for block_number in range(block_start, block_end + 1): + block_hash = self.provider.get_block_hash(block_number) + for event in self.provider.get_events(block_hash=block_hash): + + if ( + pallet_name is not None + and pallet_name != event.value["event"]["module_id"] + ): + continue + + if ( + event_name is not None + and event_name != event.value["event"]["event_id"] + ): + continue + + e = EventSubmitTransaction(block_number, block_hash, event.value) + + logging.debug(f"[BoolWatcher] _filter_events: event={e}") + + if e.cid not in self.filter_cids: + continue + + result.append(e) + + logging.info(f"[BoolWatcher] _filter_events: end [#{block_start}, #{block_end}]") + + self.db.upsert_latest_scan(block_end) + + return result + + def _verify_msg(self, cid: int, msg: bytes, signature: bytes): + return self.public_keys[cid].verify(msg, signature) + + def _query_storage(self, event): + tx_msg = TxMessage(event) + + storage_obj = self.provider.query( + module="Channel", + storage_function="TxMessages", + block_hash=tx_msg.event.block_hash, + params=[tx_msg.event.cid, tx_msg.event.verify_hash], + ) + + to_sui = True if tx_msg.event.cid in self.sui_cids else False + + tx_msg.parse_storage_obj(storage_obj, to_sui) + + if self.verify: + try: + assert self._verify_msg(tx_msg.event.cid, tx_msg.msg, tx_msg.signature) + except Exception as e: + logging.error(f"[BoolWatcher] _verify_msg: tx_msg={tx_msg}") + raise e + + self.db.add_wait_record(tx_msg.format_json()) + + logging.info(f"[BoolWatcher] _query_storage: tx_msg={tx_msg}") + + def _query_events(self): + # TODO: parallel processing + while self.begin_num < self.latest: + batch = min(self.latest - self.begin_num, self.max_block_range) + events = self._filter_events( + block_start=self.begin_num, block_end=self.begin_num + batch + ) + for event in events: + self._query_storage(event) + + self.begin_num = self.begin_num + batch + 1 + self.latest = self._latest_number() + + def run(self): + + self._query_events() + + while True: + self.latest = self.provider.subscribe_block_headers( + subscription_handler, finalized_only=True + ) + + logging.info( + f"[BoolWatcher] subscribe: begin [#{self.begin_num}, #{self.latest}]" + ) + + if self.latest <= self.begin_num: + logging.info( + f"[BoolWatcher] subscribe: waiting {self.wait_seconds} seconds for next latest block" + ) + time.sleep(self.wait_seconds) + continue + + try: + events = self._filter_events( + block_start=self.begin_num, + block_end=self.latest + ) + except ValueError: + self._query_events() + continue + except Exception as e: + logging.warning(e) + continue + + for event in events: + self._query_storage(event) + + logging.info( + f"[BoolWatcher] subscribe: end [#{self.begin_num}, #{self.latest}]" + ) + + self.begin_num = self.latest + 1 + + # waiting some seconds + time.sleep(self.wait_seconds) diff --git a/sui/scripts/dola_sui_sdk/__init__.py b/sui/scripts/dola_sui_sdk/__init__.py index 31890afc..cd55036b 100644 --- a/sui/scripts/dola_sui_sdk/__init__.py +++ b/sui/scripts/dola_sui_sdk/__init__.py @@ -42,6 +42,6 @@ def set_dola_project_path(path: Union[Path, str], network="sui-mainnet"): DOLA_CONFIG["DOLA_PROJECT_PATH"] = path DOLA_CONFIG["DOLA_SUI_PATH"] = path.joinpath("sui") assert DOLA_CONFIG["DOLA_SUI_PATH"].exists(), f"Path error:{DOLA_CONFIG['DOLA_SUI_PATH'].absolute()}!" - sui_project = sui_brownie.SuiProject(project_path=DOLA_CONFIG["DOLA_SUI_PATH"], network=network) + sui_project.__init__(project_path=DOLA_CONFIG["DOLA_SUI_PATH"], network=network) sui_project.load_config() sui_project.read_cache() diff --git a/sui/scripts/dola_sui_sdk/deploy.py b/sui/scripts/dola_sui_sdk/deploy.py index 3e5e513e..aed1e859 100644 --- a/sui/scripts/dola_sui_sdk/deploy.py +++ b/sui/scripts/dola_sui_sdk/deploy.py @@ -17,7 +17,12 @@ def export_to_config(): if "packages" not in config["networks"][current_network]: config["networks"][current_network]["packages"] = {} - config["networks"][current_network]["packages"]["dola_protocol"] = sui_project.DolaProtocol[-1] + origin = config["networks"][current_network]["packages"]["dola_protocol"].get("origin", None) + + config["networks"][current_network]["packages"]["dola_protocol"] = { + "origin": origin if origin is not None else sui_project.DolaProtocol[-1], + "latest": sui_project.DolaProtocol[-1] + } config["networks"][current_network]["packages"]["genesis_proposal"] = sui_project.GenesisProposal[-1] config["networks"][current_network]["packages"]["external_interfaces"] = sui_project.ExternalInterfaces[-1] @@ -189,7 +194,7 @@ def deploy_migrate_reward_pool(file_dir="add_group_0"): prepare_proposal(add_group.package_id) -def deploy(): +def redeploy(): dola_protocol_package = sui_brownie.SuiPackage( package_path=DOLA_CONFIG["DOLA_SUI_PATH"].joinpath("dola_protocol") ) @@ -239,6 +244,54 @@ def deploy(): )) +def first_deploy(): + dola_protocol_package = sui_brownie.SuiPackage( + package_path=DOLA_CONFIG["DOLA_SUI_PATH"].joinpath("dola_protocol") + ) + dola_protocol_package.program_publish_package(replace_address=dict( + wormhole=sui_project.network_config['packages']['wormhole'], + pyth=sui_project.network_config['packages']['pyth'] + ), replace_publish_at=dict( + wormhole=sui_project.network_config['packages']['wormhole'], + pyth=sui_project.network_config['packages']['pyth'], + ), gas_budget=1000000000) + + genesis_proposal_package = sui_brownie.SuiPackage( + package_path=DOLA_CONFIG["DOLA_SUI_PATH"].joinpath( + "proposals/genesis_proposal") + ) + genesis_proposal_package.program_publish_package(replace_address=dict( + dola_protocol=dola_protocol_package.package_id, + wormhole=sui_project.network_config['packages']['wormhole'], + pyth=sui_project.network_config['packages']['pyth'] + ), replace_publish_at=dict( + dola_protocol=dola_protocol_package.package_id, + wormhole=sui_project.network_config['packages']['wormhole'], + pyth=sui_project.network_config['packages']['pyth'], + )) + + if sui_project.network != "sui-mainnet": + test_coins_package = sui_brownie.SuiPackage( + package_path=DOLA_CONFIG["DOLA_SUI_PATH"].joinpath("test_coins") + ) + + test_coins_package.program_publish_package() + + external_interfaces_package = sui_brownie.SuiPackage( + package_path=DOLA_CONFIG["DOLA_SUI_PATH"].joinpath("external_interfaces") + ) + + external_interfaces_package.program_publish_package(replace_address=dict( + dola_protocol=dola_protocol_package.package_id, + wormhole=sui_project.network_config['packages']['wormhole'], + pyth=sui_project.network_config['packages']['pyth'] + ), replace_publish_at=dict( + dola_protocol=dola_protocol_package.package_id, + wormhole=sui_project.network_config['packages']['wormhole'], + pyth=sui_project.network_config['packages']['pyth'], + )) + + def redeploy_genesis_proposal(): genesis_proposal_package = sui_brownie.SuiPackage( package_path=DOLA_CONFIG["DOLA_SUI_PATH"].joinpath( @@ -289,7 +342,7 @@ def redeploy_reserve_proposal(): def main(): - deploy() + first_deploy() export_to_config() diff --git a/sui/scripts/dola_sui_sdk/init.py b/sui/scripts/dola_sui_sdk/init.py index 0169a114..62881eaf 100644 --- a/sui/scripts/dola_sui_sdk/init.py +++ b/sui/scripts/dola_sui_sdk/init.py @@ -37,7 +37,7 @@ def register_token_price(dola_pool_id, price, decimal): def active_governance_v1(): """Calls are required by the deployer public entry fun activate_governance( - governance_genesis: &mut GovernanceGenesis, + upgrade_cap: UpgradeCap, governance_info: &mut GovernanceInfo, ctx: &mut TxContext ) @@ -45,6 +45,8 @@ def active_governance_v1(): """ dola_protocol = load.dola_protocol_package() upgrade_cap = load.get_upgrade_cap_by_package_id(dola_protocol.package_id) + + print("executing activate_governance") dola_protocol.governance_v1.activate_governance( upgrade_cap, dola_protocol.governance_v1.GovernanceInfo[-1], @@ -80,7 +82,8 @@ def create_proposal(): """ genesis_proposal = load.genesis_proposal_package() dola_protocol = load.dola_protocol_package( - package_id=sui_project.network_config['packages']['dola_protocol']['origin']) + package_id=sui_project.network_config['packages']['dola_protocol']['origin'] + ) genesis_proposal.genesis_proposal.create_proposal( dola_protocol.governance_v1.GovernanceInfo[-1] ) @@ -541,21 +544,22 @@ def batch_execute_proposal(): init_wormhole_adapter_core_tx_block = [[ genesis_proposal.genesis_proposal.init_wormhole_adapter_core, [ - Argument("NestedResult", NestedResult(U16(0), U16(0))), - Argument("NestedResult", NestedResult(U16(0), U16(1))), + Argument("NestedResult", NestedResult(U16(0), U16(0))), # HotPotato Argument("Input", U16(2)) ], [] ]] - finish_proposal_tx_block = build_finish_proposal_tx_block(genesis_proposal, 1) + finish_proposal_tx_block = build_finish_proposal_tx_block(genesis_proposal, 1) # HotPotato + print("executing init_wormhole_adapter_core") sui_project.batch_transaction( actual_params=init_wormhole_adapter_core_params, transactions=vote_proposal_final_tx_block + init_wormhole_adapter_core_tx_block + finish_proposal_tx_block ) # Use core state + print("executing init_wormhole_adapter_pool") init_wormhole_adapter_pool() # Init poolmanager params @@ -588,6 +592,7 @@ def batch_execute_proposal(): finish_proposal_tx_block = build_finish_proposal_tx_block(genesis_proposal, pool_num) + print("executing register new pools") sui_project.batch_transaction( actual_params=basic_params + pool_params, transactions=vote_proposal_final_tx_block + register_new_pool_tx_blocks + finish_proposal_tx_block @@ -598,6 +603,8 @@ def batch_execute_proposal(): group_chain_ids = [5, 23] create_proposal() + + print("executing init system core and lending core") sui_project.batch_transaction( actual_params=[dola_protocol.governance_v1.GovernanceInfo[-1], # 0 sui_project[SuiObject.from_type(proposal())][-1], # 1 @@ -613,30 +620,26 @@ def batch_execute_proposal(): ], # 0. vote_proposal_final [ genesis_proposal.genesis_proposal.init_system_core, - [Argument("NestedResult", NestedResult(U16(0), U16(0))), - Argument("NestedResult", NestedResult(U16(0), U16(1))), + [Argument("NestedResult", NestedResult(U16(0), U16(0))), # HotPotato Argument("Input", U16(2))], [] ], # 1. init_system_core [ genesis_proposal.genesis_proposal.init_lending_core, - [Argument("NestedResult", NestedResult(U16(1), U16(0))), - Argument("NestedResult", NestedResult(U16(1), U16(1))), + [Argument("NestedResult", NestedResult(U16(1), U16(0))), # HotPotato Argument("Input", U16(2))], [] ], # 2. init_lending_core [ genesis_proposal.genesis_proposal.init_chain_group_id, - [Argument("NestedResult", NestedResult(U16(2), U16(0))), - Argument("NestedResult", NestedResult(U16(2), U16(1))), + [Argument("NestedResult", NestedResult(U16(2), U16(0))), # HotPotato Argument("Input", U16(3)), Argument("Input", U16(4)), Argument("Input", U16(5))], [] ], # 3. init_chain_group_id [genesis_proposal.genesis_proposal.destory, - [Argument("NestedResult", NestedResult(U16(3), U16(0))), - Argument("NestedResult", NestedResult(U16(3), U16(1)))], + [Argument("NestedResult", NestedResult(U16(3), U16(0)))], # HotPotato [] ] ] @@ -694,12 +697,111 @@ def batch_execute_proposal(): actual_params = basic_params + reserve_params transactions = vote_proposal_final_tx_block + register_new_reserve_tx_blocks + finish_proposal_tx_block + print("executing register_new_reserve") sui_project.batch_transaction( actual_params=actual_params, transactions=transactions ) +def init_bool_adapter_core_with_key(): + # public fun init_bool_adapter_core_with_key( + # hot_potato: HotPotato, + # governance_genesis: &GovernanceGenesis, + # committee_pk: vector, + # bool_global: &mut GlobalState, + # init_relayer: address, + # ctx: &mut TxContext + # ): HotPotato + + genesis_proposal = load.genesis_proposal_package() + dola_protocol = load.dola_protocol_package() + + # init_bool_adapter_core_with_key + + governance_genesis = dola_protocol.genesis.GovernanceGenesis[-1] + committee_key = sui_project.network_config['bool_network']['committee_key'] + bool_global = sui_project.network_config['bool_network']['global_state'] + init_relayer = sui_project.account.address() + + create_proposal() + + init_bool_adapter_core_params = [ + dola_protocol.governance_v1.GovernanceInfo[-1], # 0 + sui_project[SuiObject.from_type(proposal())][-1], # 1 + governance_genesis, # 2 + list(bytes.fromhex(committee_key.replace("0x", ""))), # 3 + bool_global, # 4 + init_relayer # 5 + ] + + vote_proposal_final_tx_block = build_vote_proposal_final_tx_block(genesis_proposal) + + init_bool_adapter_core_tx_block = [[ + genesis_proposal.genesis_proposal.init_bool_adapter_core_with_key, + [ + Argument("NestedResult", NestedResult(U16(0), U16(0))), # HotPotato + Argument("Input", U16(2)), + Argument("Input", U16(3)), + Argument("Input", U16(4)), + Argument("Input", U16(5)) + ], + [] + ]] + + finish_proposal_tx_block = build_finish_proposal_tx_block(genesis_proposal, 1) # HotPotato + + sui_project.batch_transaction( + actual_params=init_bool_adapter_core_params, + transactions=vote_proposal_final_tx_block + init_bool_adapter_core_tx_block + finish_proposal_tx_block + ) + + +def init_bool_adapter_core_with_cap(anchor_cap): + # public fun init_bool_adapter_core_with_cap( + # hot_potato: HotPotato, + # bool_anchor_cap: AnchorCap, + # init_relayer: address, + # ctx: &mut TxContext + # ): HotPotato + + genesis_proposal = load.genesis_proposal_package() + dola_protocol = load.dola_protocol_package() + + # init_bool_adapter_core_with_cap + + governance_genesis = dola_protocol.genesis.GovernanceGenesis[-1] + init_relayer = sui_project.account.address() + + create_proposal() + + init_bool_adapter_core_params = [ + dola_protocol.governance_v1.GovernanceInfo[-1], # 0 + sui_project[SuiObject.from_type(proposal())][-1], # 1 + anchor_cap, # 2 + init_relayer # 3 + ] + + vote_proposal_final_tx_block = build_vote_proposal_final_tx_block(genesis_proposal) + + init_bool_adapter_core_tx_block = [[ + genesis_proposal.genesis_proposal.init_bool_adapter_core_with_cap, + [ + Argument("NestedResult", NestedResult(U16(0), U16(0))), # HotPotato + Argument("Input", U16(2)), + Argument("Input", U16(3)), + ], + [] + ]] + + finish_proposal_tx_block = build_finish_proposal_tx_block(genesis_proposal, 1) # HotPotato + + sui_project.batch_transaction( + actual_params=init_bool_adapter_core_params, + transactions=vote_proposal_final_tx_block + init_bool_adapter_core_tx_block + finish_proposal_tx_block + ) + + def register_new_reserve(reserve: str = "MATIC"): genesis_proposal = load.genesis_proposal_package() create_proposal() @@ -781,6 +883,8 @@ def batch_create_pool(): build_create_pool_tx_block(dola_protocol, i, coin_types[i]) for i in range(len(create_pool_params)) ] + + print("executing batch_create_pool") sui_project.batch_transaction( actual_params=create_pool_params, transactions=create_pool_tx_blocks @@ -807,9 +911,9 @@ def batch_init_oracle(): genesis_proposal = load.genesis_proposal_package() create_proposal() - # btc_token_param = construct_register_token_price_param( - # "BTC/USD", 'BTC' - # ) + btc_token_param = construct_register_token_price_param( + "BTC/USD", 'BTC' + ) # usdt_token_param = construct_register_token_price_param( # "USDT/USD", 'USDT' # ) @@ -831,9 +935,9 @@ def batch_init_oracle(): # arb_token_param = construct_register_token_price_param( # "ARB/USD", 'ARB' # ) - whusdceth_token_param = construct_register_token_price_param( - "USDC/USD", 'whUSDCeth' - ) + # whusdceth_token_param = construct_register_token_price_param( + # "USDC/USD", 'whUSDCeth' + # ) basic_params = [ sui_project.network_config["objects"]["GovernanceInfo"], # 0 @@ -843,7 +947,7 @@ def batch_init_oracle(): ] # token_params = btc_token_param + usdt_token_param + usdc_token_param + sui_token_param + eth_token_param + matic_token_param + op_token_param + arb_token_param - token_params = whusdceth_token_param + token_params = btc_token_param token_nums = len(token_params) // 4 register_token_price_tx_blocks = [ @@ -856,6 +960,7 @@ def batch_init_oracle(): finish_proposal_tx_block = build_finish_proposal_tx_block(genesis_proposal, token_nums) + print("executing batch_init_oracle") sui_project.batch_transaction( actual_params=basic_params + token_params, transactions=vote_proposal_final_tx_block + register_token_price_tx_blocks + finish_proposal_tx_block @@ -1617,7 +1722,7 @@ def batch_init(): batch_init_oracle() batch_create_pool() batch_execute_proposal() - + init_bool_adapter_core_with_key() if __name__ == '__main__': batch_init() diff --git a/sui/scripts/dola_sui_sdk/lendingBool.py b/sui/scripts/dola_sui_sdk/lendingBool.py new file mode 100644 index 00000000..45114b00 --- /dev/null +++ b/sui/scripts/dola_sui_sdk/lendingBool.py @@ -0,0 +1,1034 @@ +from sui_brownie import Argument, U16, NestedResult + +import config +from dola_sui_sdk import load, init +from dola_sui_sdk.exchange import ExchangeManager +from dola_sui_sdk.load import sui_project +from dola_sui_sdk.oracle import get_feed_vaa + +U64_MAX = 18446744073709551615 + +exchange_manager = ExchangeManager() + +# copy from LibBoolAdapterVerify.sol +SERVER_OPCODE_SYSTEM_BINDING = 0 +SERVER_OPCODE_SYSTEM_UNBINDING = 1 +SERVER_OPCODE_LENDING_SUPPLY = 2 +SERVER_OPCODE_LENDING_WITHDRAW = 3 +SERVER_OPCODE_LENDING_BORROW = 4 +SERVER_OPCODE_LENDING_REPAY = 5 +SERVER_OPCODE_LENDING_LIQUIDATE = 6 +SERVER_OPCODE_LENDING_COLLATERAL = 7 +SERVER_OPCODE_LENDING_CANCEL_COLLATERAL = 8 + + +def dispatch(message_raw: str, signature: str): + msg_bytes = list(bytes.fromhex(message_raw.replace('0x', ''))) + opcode = msg_bytes[-1] + + if SERVER_OPCODE_SYSTEM_BINDING == opcode: + core_binding(message_raw, signature) + elif SERVER_OPCODE_SYSTEM_UNBINDING == opcode: + core_unbinding(message_raw, signature) + elif SERVER_OPCODE_LENDING_SUPPLY == opcode: + core_supply(message_raw, signature) + elif SERVER_OPCODE_LENDING_WITHDRAW == opcode: + core_withdraw(message_raw, signature) + elif SERVER_OPCODE_LENDING_BORROW == opcode: + core_borrow(message_raw, signature) + elif SERVER_OPCODE_LENDING_REPAY == opcode: + core_repay(message_raw, signature) + elif SERVER_OPCODE_LENDING_LIQUIDATE == opcode: + core_liquidate(message_raw, signature) + elif SERVER_OPCODE_LENDING_COLLATERAL == opcode: + core_as_collateral(message_raw, signature) + elif SERVER_OPCODE_LENDING_CANCEL_COLLATERAL == opcode: + core_cancel_as_collateral(message_raw, signature) + else: + print(f"lendingBool dispatch: unexpect opcode={opcode}") + raise ValueError + + +def dola_pool_id_to_symbol(pool_id): + return config.DOLA_POOL_ID_TO_SYMBOL[pool_id] + + +def calculate_sui_gas(gas_used): + return int(gas_used['computationCost']) + int(gas_used['storageCost']) - int( + gas_used['storageRebate']) + + +def feed_multi_token_price_with_fee(asset_ids, relay_fee=0, fee_rate=0.8): + dola_protocol = load.dola_protocol_package() + + governance_genesis = sui_project.network_config['objects']['GovernanceGenesis'] + wormhole_state = sui_project.network_config['objects']['WormholeState'] + price_oracle = sui_project.network_config['objects']['PriceOracle'] + pyth_state = sui_project.network_config['objects']['PythState'] + pyth_fee_amount = 1 + + feed_gas = 0 + + symbols = [config.DOLA_POOL_ID_TO_SYMBOL[pool_id] for pool_id in asset_ids] + price_info_objects = [config.DOLA_POOL_ID_TO_PRICE_INFO_OBJECT[pool_id] for pool_id in asset_ids] + vaas = [get_feed_vaa(symbol) for symbol in symbols] + for (pool_id, symbol, price_info_object) in zip(asset_ids, symbols, price_info_objects): + vaa = get_feed_vaa(symbol) + result = sui_project.batch_transaction_inspect( + actual_params=[ + governance_genesis, + wormhole_state, + pyth_state, + price_info_object, + price_oracle, + pool_id, + list(bytes.fromhex(vaa.replace("0x", ""))), + init.clock(), + pyth_fee_amount + ], + transactions=[ + [ + dola_protocol.oracle.feed_token_price_by_pyth_v2, + [ + Argument("Input", U16(0)), + Argument("Input", U16(1)), + Argument("Input", U16(2)), + Argument("Input", U16(3)), + Argument("Input", U16(4)), + Argument("Input", U16(5)), + Argument("Input", U16(6)), + Argument("Input", U16(7)), + Argument("Input", U16(8)), + ], + [] + ], + [ + dola_protocol.oracle.get_token_price, + [ + Argument("Input", U16(4)), + Argument("Input", U16(5)), + ], + [] + ] + ] + ) + + decimal = int(result['results'][2]['returnValues'][1][0][0]) + + pyth_price = parse_u256(result['results'][2]['returnValues'][0][0]) / (10 ** decimal) + + if f"{symbol}T" in config.EXCHANGE_SYMBOLS: + exchange_price = exchange_manager.fetch_fastest_ticker(f"{symbol}T")['close'] + else: + exchange_price = 1 + + if pyth_price > exchange_price: + deviation = 1 - exchange_price / pyth_price + else: + deviation = 1 - pyth_price / exchange_price + + deviation_threshold = config.SYMBOL_TO_DEVIATION[symbol] + if deviation > deviation_threshold: + print(f"The oracle price difference is too large! {symbol} deviation {deviation}!") + # raise ValueError(f"The oracle price difference is too large! {symbol} deviation {deviation}!") + + gas = calculate_sui_gas(result['effects']['gasUsed']) + feed_gas += gas + + if relay_fee >= int(fee_rate * feed_gas): + relay_fee -= int(fee_rate * feed_gas) + for (pool_id, vaa, symbol, price_info_object) in zip(asset_ids, vaas, symbols, price_info_objects): + sui_project.batch_transaction( + actual_params=[ + governance_genesis, + wormhole_state, + pyth_state, + price_info_object, + price_oracle, + pool_id, + list(bytes.fromhex(vaa.replace("0x", ""))), + init.clock(), + pyth_fee_amount + ], + transactions=[ + [ + dola_protocol.oracle.feed_token_price_by_pyth_v2, + [ + Argument("Input", U16(0)), + Argument("Input", U16(1)), + Argument("Input", U16(2)), + Argument("Input", U16(3)), + Argument("Input", U16(4)), + Argument("Input", U16(5)), + Argument("Input", U16(6)), + Argument("Input", U16(7)), + Argument("Input", U16(8)), + ], + [] + ] + ] + ) + return relay_fee, feed_gas + + +def get_zero_coin(): + sui_coins = sui_project.get_account_sui() + if len(sui_coins) == 1: + result = sui_project.pay_sui([0]) + return result['effects']['created'][0]['reference']['objectId'] + elif len(sui_coins) == 2 and 0 in [coin['balance'] for coin in sui_coins.values()]: + return [coin_object for coin_object, coin in sui_coins.items() if coin['balance'] == "0"][0] + else: + sui_project.pay_all_sui() + result = sui_project.pay_sui([0]) + return result['effects']['created'][0]['reference']['objectId'] + + +def get_amount_coins_if_exist(amounts: [int]): + amounts = [int(amount) for amount in amounts] + sui_project.pay_sui(amounts) + sui_coins = sui_project.get_account_sui() + coin_objects = list(sui_coins.keys()) + balances = [int(sui_coins[coin_object]["balance"]) for coin_object in coin_objects] + coins = [] + for amount in amounts: + index = balances.index(amount) + coins.append(coin_objects[index]) + del balances[index] + del coin_objects[index] + + return coins + + +def get_owned_zero_coin(): + sui_coins = sui_project.get_account_sui() + return [coin_object for coin_object, coin in sui_coins.items() if coin['balance'] == '0'][0] + + +def get_feed_tokens_for_relayer_bool(message_raw): + """ + public fun get_feed_tokens_for_relayer_bool( + pool_manager_info: &mut PoolManagerInfo, + user_manager_info: &mut UserManagerInfo, + storage: &mut Storage, + price_oracle: &mut PriceOracle, + message_raw: vector, + clock: &Clock + ) + :return: + """ + external_interface = load.external_interfaces_package() + + pool_manager_info = sui_project.network_config['objects']['PoolManagerInfo'] + user_manager_info = sui_project.network_config['objects']['UserManagerInfo'] + lending_storage = sui_project.network_config['objects']['LendingStorage'] + price_oracle = sui_project.network_config['objects']['PriceOracle'] + + result = external_interface.interfaces.get_feed_tokens_for_relayer_bool.inspect( + pool_manager_info, + user_manager_info, + lending_storage, + price_oracle, + list(bytes.fromhex(message_raw.replace('0x', ''))), + init.clock() + ) + + if 'results' not in result: + return [] + + feed_token_ids = convert_vec_u16_to_list(result['results'][0]['returnValues'][0][0]) + feed_token_ids = list(set(feed_token_ids)) + if len(result['results'][0]['returnValues']) == 2: + skip_token_ids = convert_vec_u16_to_list(result['results'][0]['returnValues'][1][0]) + else: + skip_token_ids = [] + + return [x for x in feed_token_ids if x not in skip_token_ids] + + +def core_supply(message_raw, signature, relay_fee=0, fee_rate=0.8): + """ + public entry fun supply( + genesis: &GovernanceGenesis, + pool_manager_info: &mut PoolManagerInfo, + user_manager_info: &mut UserManagerInfo, + bool_global: &mut GlobalState, + core_state: &mut CoreState, + oracle: &mut PriceOracle, + storage: &mut Storage, + message_raw: vector, + signature: vector, + clock: &Clock, + ctx: &mut TxContext + ) + :param message_raw: + :param signature: + :return: + """ + dola_protocol = load.dola_protocol_package() + + genesis = sui_project.network_config['objects']['GovernanceGenesis'] + pool_manager_info = sui_project.network_config['objects']['PoolManagerInfo'] + user_manager_info = sui_project.network_config['objects']['UserManagerInfo'] + oracle = sui_project.network_config['objects']['PriceOracle'] + storage = sui_project.network_config['objects']['LendingStorage'] + + bool_global = sui_project.network_config['bool_network']['global_state'] + core_state = sui_project.network_config['bool_network']['core_state'] + + result = dola_protocol.lending_core_bool_adapter.supply.simulate( + genesis, + pool_manager_info, + user_manager_info, + bool_global, + core_state, + oracle, + storage, + list(bytes.fromhex(message_raw.replace('0x', ''))), + list(bytes.fromhex(signature.replace('0x', ''))), + init.clock(), + ) + gas = calculate_sui_gas(result['effects']['gasUsed']) + status = result['effects']['status']['status'] + + executed = False + if relay_fee >= int(fee_rate * gas): + executed = True + result = dola_protocol.lending_core_bool_adapter.supply( + genesis, + pool_manager_info, + user_manager_info, + bool_global, + core_state, + oracle, + storage, + list(bytes.fromhex(message_raw.replace('0x', ''))), + list(bytes.fromhex(signature.replace('0x', ''))), + init.clock(), + ) + return gas, executed, status, result['effects']['transactionDigest'] + elif status == 'failure': + return gas, executed, result['effects']['status']['error'], "" + else: + return gas, executed, status, "" + + +def core_withdraw(message_raw, signature, relay_fee=0, fee_rate=0.8): + """ + public entry fun withdraw( + genesis: &GovernanceGenesis, + pool_manager_info: &mut PoolManagerInfo, + user_manager_info: &mut UserManagerInfo, + bool_global: &mut GlobalState, + core_state: &mut CoreState, + oracle: &mut PriceOracle, + storage: &mut Storage, + bool_message_fee: Coin, + message_raw: vector, + signature: vector, + clock: &Clock, + ctx: &mut TxContext + ) + :return: + """ + dola_protocol = load.dola_protocol_package() + + genesis = sui_project.network_config['objects']['GovernanceGenesis'] + pool_manager_info = sui_project.network_config['objects']['PoolManagerInfo'] + user_manager_info = sui_project.network_config['objects']['UserManagerInfo'] + oracle = sui_project.network_config['objects']['PriceOracle'] + storage = sui_project.network_config['objects']['LendingStorage'] + + bool_global = sui_project.network_config['bool_network']['global_state'] + core_state = sui_project.network_config['bool_network']['core_state'] + + asset_ids = get_feed_tokens_for_relayer_bool(message_raw) + feed_nums = len(asset_ids) + + if feed_nums > 0: + left_relay_fee, feed_gas = feed_multi_token_price_with_fee(asset_ids, relay_fee, fee_rate) + else: + left_relay_fee = relay_fee + feed_gas = 0 + + result = sui_project.batch_transaction_simulate( + actual_params=[ + genesis, + pool_manager_info, + user_manager_info, + bool_global, + core_state, + oracle, + storage, + list(bytes.fromhex(message_raw.replace('0x', ''))), + list(bytes.fromhex(signature.replace('0x', ''))), + init.clock(), + ], + transactions=[ + [ + dola_protocol.lending_core_bool_adapter.calc_withdrow_bool_message_fee, + [ + Argument("Input", U16(3)), + Argument("Input", U16(4)), + list(bytes.fromhex(message_raw.replace('0x', ''))), + ], + [] + ], + [ + dola_protocol.lending_core_bool_adapter.withdraw, + [ + Argument("Input", U16(0)), + Argument("Input", U16(1)), + Argument("Input", U16(2)), + Argument("Input", U16(3)), + Argument("Input", U16(4)), + Argument("Input", U16(5)), + Argument("Input", U16(6)), + Argument("NestedResult", NestedResult(U16(0), U16(0))), + Argument("Input", U16(7)), + Argument("Input", U16(8)), + ], + [] + ] + ] + ) + + status = result['effects']['status']['status'] + gas = calculate_sui_gas(result['effects']['gasUsed']) + executed = False + if left_relay_fee >= int(fee_rate * gas) and status == 'success': + executed = True + result = sui_project.batch_transaction( + actual_params=[ + genesis, + pool_manager_info, + user_manager_info, + bool_global, + core_state, + oracle, + storage, + list(bytes.fromhex(message_raw.replace('0x', ''))), + list(bytes.fromhex(signature.replace('0x', ''))), + init.clock(), + ], + transactions=[ + [ + dola_protocol.lending_core_bool_adapter.calc_withdrow_bool_message_fee, + [ + Argument("Input", U16(3)), + Argument("Input", U16(4)), + list(bytes.fromhex(message_raw.replace('0x', ''))), + ], + [] + ], + [ + dola_protocol.lending_core_bool_adapter.withdraw, + [ + Argument("Input", U16(0)), + Argument("Input", U16(1)), + Argument("Input", U16(2)), + Argument("Input", U16(3)), + Argument("Input", U16(4)), + Argument("Input", U16(5)), + Argument("Input", U16(6)), + Argument("NestedResult", NestedResult(U16(0), U16(0))), + Argument("Input", U16(7)), + Argument("Input", U16(8)), + ], + [] + ] + ] + ) + return gas + feed_gas, executed, status, feed_nums, result['effects']['transactionDigest'] + elif status == 'failure': + return gas + feed_gas, executed, result['effects']['status']['error'], feed_nums, "" + else: + return gas + feed_gas, executed, status, feed_nums, "" + + +def core_borrow(message_raw, signature, relay_fee=0, fee_rate=0.8): + """ + public entry fun borrow( + genesis: &GovernanceGenesis, + pool_manager_info: &mut PoolManagerInfo, + user_manager_info: &mut UserManagerInfo, + bool_global: &mut GlobalState, + core_state: &mut CoreState, + oracle: &mut PriceOracle, + storage: &mut Storage, + bool_message_fee: Coin, + message_raw: vector, + signature: vector, + clock: &Clock, + ctx: &mut TxContext + ) + :return: + """ + dola_protocol = load.dola_protocol_package() + + genesis = sui_project.network_config['objects']['GovernanceGenesis'] + pool_manager_info = sui_project.network_config['objects']['PoolManagerInfo'] + user_manager_info = sui_project.network_config['objects']['UserManagerInfo'] + oracle = sui_project.network_config['objects']['PriceOracle'] + storage = sui_project.network_config['objects']['LendingStorage'] + + bool_global = sui_project.network_config['bool_network']['global_state'] + core_state = sui_project.network_config['bool_network']['core_state'] + + asset_ids = get_feed_tokens_for_relayer_bool(message_raw) + feed_nums = len(asset_ids) + + if feed_nums > 0: + left_relay_fee, feed_gas = feed_multi_token_price_with_fee(asset_ids, relay_fee, fee_rate) + else: + left_relay_fee = relay_fee + feed_gas = 0 + + result = sui_project.batch_transaction_simulate( + actual_params=[ + genesis, + pool_manager_info, + user_manager_info, + bool_global, + core_state, + oracle, + storage, + list(bytes.fromhex(message_raw.replace('0x', ''))), + list(bytes.fromhex(signature.replace('0x', ''))), + init.clock(), + ], + transactions=[ + [ + dola_protocol.lending_core_bool_adapter.calc_withdrow_bool_message_fee, + [ + Argument("Input", U16(3)), + Argument("Input", U16(4)), + list(bytes.fromhex(message_raw.replace('0x', ''))), + ], + [] + ], + [ + dola_protocol.lending_core_bool_adapter.borrow, + [ + Argument("Input", U16(0)), + Argument("Input", U16(1)), + Argument("Input", U16(2)), + Argument("Input", U16(3)), + Argument("Input", U16(4)), + Argument("Input", U16(5)), + Argument("Input", U16(6)), + Argument("NestedResult", NestedResult(U16(0), U16(0))), + Argument("Input", U16(7)), + Argument("Input", U16(8)), + ], + [] + ] + ] + ) + + status = result['effects']['status']['status'] + gas = calculate_sui_gas(result['effects']['gasUsed']) + executed = False + if left_relay_fee >= int(fee_rate * gas) and status == 'success': + executed = True + result = sui_project.batch_transaction( + actual_params=[ + genesis, + pool_manager_info, + user_manager_info, + bool_global, + core_state, + oracle, + storage, + list(bytes.fromhex(message_raw.replace('0x', ''))), + list(bytes.fromhex(signature.replace('0x', ''))), + init.clock(), + ], + transactions=[ + [ + dola_protocol.lending_core_bool_adapter.calc_withdrow_bool_message_fee, + [ + Argument("Input", U16(3)), + Argument("Input", U16(4)), + list(bytes.fromhex(message_raw.replace('0x', ''))), + ], + [] + ], + [ + dola_protocol.lending_core_bool_adapter.borrow, + [ + Argument("Input", U16(0)), + Argument("Input", U16(1)), + Argument("Input", U16(2)), + Argument("Input", U16(3)), + Argument("Input", U16(4)), + Argument("Input", U16(5)), + Argument("Input", U16(6)), + Argument("NestedResult", NestedResult(U16(0), U16(0))), + Argument("Input", U16(7)), + Argument("Input", U16(8)), + ], + [] + ] + ] + ) + return gas + feed_gas, executed, status, feed_nums, result['effects']['transactionDigest'] + elif status == 'failure': + return gas + feed_gas, executed, result['effects']['status']['error'], feed_nums, "" + else: + return gas + feed_gas, executed, status, feed_nums, "" + + +def core_repay(message_raw, signature, relay_fee=0, fee_rate=0.8): + """ + public entry fun repay( + genesis: &GovernanceGenesis, + pool_manager_info: &mut PoolManagerInfo, + user_manager_info: &mut UserManagerInfo, + bool_global: &mut GlobalState, + core_state: &mut CoreState, + oracle: &mut PriceOracle, + storage: &mut Storage, + message_raw: vector, + signature: vector, + clock: &Clock, + ctx: &mut TxContext + ) + :return: + """ + dola_protocol = load.dola_protocol_package() + + genesis = sui_project.network_config['objects']['GovernanceGenesis'] + pool_manager_info = sui_project.network_config['objects']['PoolManagerInfo'] + user_manager_info = sui_project.network_config['objects']['UserManagerInfo'] + oracle = sui_project.network_config['objects']['PriceOracle'] + storage = sui_project.network_config['objects']['LendingStorage'] + + bool_global = sui_project.network_config['bool_network']['global_state'] + core_state = sui_project.network_config['bool_network']['core_state'] + + result = dola_protocol.lending_core_bool_adapter.repay.simulate( + genesis, + pool_manager_info, + user_manager_info, + bool_global, + core_state, + oracle, + storage, + list(bytes.fromhex(message_raw.replace('0x', ''))), + list(bytes.fromhex(signature.replace('0x', ''))), + init.clock(), + ) + + gas = calculate_sui_gas(result['effects']['gasUsed']) + status = result['effects']['status']['status'] + + executed = False + if relay_fee >= int(fee_rate * gas): + executed = True + result = dola_protocol.lending_core_bool_adapter.repay( + genesis, + pool_manager_info, + user_manager_info, + bool_global, + core_state, + oracle, + storage, + list(bytes.fromhex(message_raw.replace('0x', ''))), + list(bytes.fromhex(signature.replace('0x', ''))), + init.clock(), + ) + return gas, executed, status, result['effects']['transactionDigest'] + elif status == 'failure': + return gas, executed, result['effects']['status']['error'], "" + else: + return gas, executed, status, "" + + +def core_liquidate(message_raw, signature, relay_fee=0, fee_rate=0.8): + """ + public entry fun liquidate( + genesis: &GovernanceGenesis, + pool_manager_info: &mut PoolManagerInfo, + user_manager_info: &mut UserManagerInfo, + bool_global: &mut GlobalState, + core_state: &mut CoreState, + oracle: &mut PriceOracle, + storage: &mut Storage, + message_raw: vector, + signature: vector, + clock: &Clock, + ctx: &mut TxContext + ) + :return: + """ + dola_protocol = load.dola_protocol_package() + + genesis = sui_project.network_config['objects']['GovernanceGenesis'] + pool_manager_info = sui_project.network_config['objects']['PoolManagerInfo'] + user_manager_info = sui_project.network_config['objects']['UserManagerInfo'] + oracle = sui_project.network_config['objects']['PriceOracle'] + storage = sui_project.network_config['objects']['LendingStorage'] + + bool_global = sui_project.network_config['bool_network']['global_state'] + core_state = sui_project.network_config['bool_network']['core_state'] + + asset_ids = get_feed_tokens_for_relayer_bool(message_raw) + feed_nums = len(asset_ids) + + if feed_nums > 0: + left_relay_fee, feed_gas = feed_multi_token_price_with_fee(asset_ids, relay_fee, fee_rate) + else: + left_relay_fee = relay_fee + feed_gas = 0 + + result = sui_project.batch_transaction_simulate( + actual_params=[ + genesis, + pool_manager_info, + user_manager_info, + bool_global, + core_state, + oracle, + storage, + list(bytes.fromhex(message_raw.replace('0x', ''))), + list(bytes.fromhex(signature.replace('0x', ''))), + init.clock(), + ], + transactions=[ + [ + dola_protocol.lending_core_bool_adapter.liquidate, + [ + Argument("Input", U16(0)), + Argument("Input", U16(1)), + Argument("Input", U16(2)), + Argument("Input", U16(3)), + Argument("Input", U16(4)), + Argument("Input", U16(5)), + Argument("Input", U16(6)), + Argument("Input", U16(7)), + Argument("Input", U16(8)), + Argument("Input", U16(9)), + ], + [] + ] + ] + ) + + status = result['effects']['status']['status'] + gas = calculate_sui_gas(result['effects']['gasUsed']) + executed = False + whitelist = [7523, 72, 5] + if int(result['events'][-1]['parsedJson']["sender_user_id"]) not in whitelist: + return gas + feed_gas, executed, status, feed_nums, "NotWhiteList" + elif left_relay_fee >= int(fee_rate * gas) and status == 'success': + executed = True + result = sui_project.batch_transaction( + actual_params=[ + genesis, + pool_manager_info, + user_manager_info, + bool_global, + core_state, + oracle, + storage, + list(bytes.fromhex(message_raw.replace('0x', ''))), + list(bytes.fromhex(signature.replace('0x', ''))), + init.clock(), + ], + transactions=[ + [ + dola_protocol.lending_core_bool_adapter.liquidate, + [ + Argument("Input", U16(0)), + Argument("Input", U16(1)), + Argument("Input", U16(2)), + Argument("Input", U16(3)), + Argument("Input", U16(4)), + Argument("Input", U16(5)), + Argument("Input", U16(6)), + Argument("Input", U16(7)), + Argument("Input", U16(8)), + Argument("Input", U16(9)), + ], + [] + ] + ] + ) + return gas + feed_gas, executed, status, feed_nums, result['effects']['transactionDigest'] + elif status == 'failure': + return gas + feed_gas, executed, result['effects']['status']['error'], feed_nums, "" + else: + return gas + feed_gas, executed, status, feed_nums, "" + + +def core_as_collateral(message_raw, signature, relay_fee=0, fee_rate=0.8): + """ + public entry fun as_collateral( + genesis: &GovernanceGenesis, + pool_manager_info: &mut PoolManagerInfo, + user_manager_info: &mut UserManagerInfo, + bool_global: &mut GlobalState, + core_state: &mut CoreState, + oracle: &mut PriceOracle, + storage: &mut Storage, + message_raw: vector, + signature: vector, + clock: &Clock, + ctx: &mut TxContext + ) + :return: + """ + dola_protocol = load.dola_protocol_package() + + genesis = sui_project.network_config['objects']['GovernanceGenesis'] + pool_manager_info = sui_project.network_config['objects']['PoolManagerInfo'] + user_manager_info = sui_project.network_config['objects']['UserManagerInfo'] + oracle = sui_project.network_config['objects']['PriceOracle'] + storage = sui_project.network_config['objects']['LendingStorage'] + + bool_global = sui_project.network_config['bool_network']['global_state'] + core_state = sui_project.network_config['bool_network']['core_state'] + + result = dola_protocol.lending_core_bool_adapter.as_collateral.simulate( + genesis, + pool_manager_info, + user_manager_info, + bool_global, + core_state, + oracle, + storage, + list(bytes.fromhex(message_raw.replace('0x', ''))), + list(bytes.fromhex(signature.replace('0x', ''))), + init.clock() + ) + + gas = calculate_sui_gas(result['effects']['gasUsed']) + status = result['effects']['status']['status'] + executed = False + if relay_fee >= int(fee_rate * gas): + executed = True + dola_protocol.lending_core_bool_adapter.as_collateral( + genesis, + pool_manager_info, + user_manager_info, + bool_global, + core_state, + oracle, + storage, + list(bytes.fromhex(message_raw.replace('0x', ''))), + list(bytes.fromhex(signature.replace('0x', ''))), + init.clock() + ) + return gas, executed, status, result['effects']['transactionDigest'] + elif status == 'failure': + return gas, executed, result['effects']['status']['error'], "" + else: + return gas, executed, status, "" + + +def core_cancel_as_collateral(message_raw, signature, relay_fee=0, fee_rate=0.8): + """ + public entry fun cancel_as_collateral( + genesis: &GovernanceGenesis, + pool_manager_info: &mut PoolManagerInfo, + user_manager_info: &mut UserManagerInfo, + bool_global: &mut GlobalState, + core_state: &mut CoreState, + oracle: &mut PriceOracle, + storage: &mut Storage, + message_raw: vector, + signature: vector, + clock: &Clock, + ctx: &mut TxContext + ) + :return: + """ + dola_protocol = load.dola_protocol_package() + + genesis = sui_project.network_config['objects']['GovernanceGenesis'] + pool_manager_info = sui_project.network_config['objects']['PoolManagerInfo'] + user_manager_info = sui_project.network_config['objects']['UserManagerInfo'] + oracle = sui_project.network_config['objects']['PriceOracle'] + storage = sui_project.network_config['objects']['LendingStorage'] + + bool_global = sui_project.network_config['bool_network']['global_state'] + core_state = sui_project.network_config['bool_network']['core_state'] + + asset_ids = get_feed_tokens_for_relayer_bool(message_raw) + feed_nums = len(asset_ids) + + if feed_nums > 0: + left_relay_fee, feed_gas = feed_multi_token_price_with_fee(asset_ids, relay_fee, fee_rate) + else: + left_relay_fee = relay_fee + feed_gas = 0 + + result = dola_protocol.lending_core_bool_adapter.cancel_as_collateral.simulate( + genesis, + pool_manager_info, + user_manager_info, + bool_global, + core_state, + oracle, + storage, + list(bytes.fromhex(message_raw.replace('0x', ''))), + list(bytes.fromhex(signature.replace('0x', ''))), + init.clock() + ) + + status = result['effects']['status']['status'] + gas = calculate_sui_gas(result['effects']['gasUsed']) + executed = False + if left_relay_fee >= int(fee_rate * gas) and status == 'success': + executed = True + result = dola_protocol.lending_core_bool_adapter.cancel_as_collateral( + genesis, + pool_manager_info, + user_manager_info, + bool_global, + core_state, + oracle, + storage, + list(bytes.fromhex(message_raw.replace('0x', ''))), + list(bytes.fromhex(signature.replace('0x', ''))), + init.clock() + ) + + return gas + feed_gas, executed, status, feed_nums, result['effects']['transactionDigest'] + elif status == 'failure': + return gas + feed_gas, executed, result['effects']['status']['error'], feed_nums, "" + else: + return gas + feed_gas, executed, status, feed_nums, "" + + +def core_binding(message_raw, signature, relay_fee=0, fee_rate=0.8): + """ + public fun bind_user_address( + genesis: &GovernanceGenesis, + user_manager_info: &mut UserManagerInfo, + bool_state: &mut GlobalState, + core_state: &mut CoreState, + storage: &Storage, + message_raw: vector, + signature: vector, + ctx: &mut TxContext + ) + :return: + """ + dola_protocol = load.dola_protocol_package() + + genesis = sui_project.network_config['objects']['GovernanceGenesis'] + user_manager_info = sui_project.network_config['objects']['UserManagerInfo'] + system_storage = sui_project.network_config['objects']['SystemStorage'] + + bool_global = sui_project.network_config['bool_network']['global_state'] + core_state = sui_project.network_config['bool_network']['core_state'] + + result = dola_protocol.system_core_bool_adapter.bind_user_address.simulate( + genesis, + user_manager_info, + bool_global, + core_state, + system_storage, + list(bytes.fromhex(message_raw.replace('0x', ''))), + list(bytes.fromhex(signature.replace('0x', ''))), + ) + + gas = calculate_sui_gas(result['effects']['gasUsed']) + + status = result['effects']['status']['status'] + executed = False + if relay_fee >= int(fee_rate * gas): + executed = True + result = dola_protocol.system_core_bool_adapter.bind_user_address( + genesis, + user_manager_info, + bool_global, + core_state, + system_storage, + list(bytes.fromhex(message_raw.replace('0x', ''))), + list(bytes.fromhex(signature.replace('0x', ''))), + ) + return gas, executed, status, result['effects']['transactionDigest'] + elif status == 'failure': + return gas, executed, result['effects']['status']['error'], "" + else: + return gas, executed, status, "" + + +def core_unbinding(message_raw, signature, relay_fee=0, fee_rate=0.8): + """ + public fun unbind_user_address( + genesis: &GovernanceGenesis, + user_manager_info: &mut UserManagerInfo, + bool_state: &mut GlobalState, + core_state: &mut CoreState, + storage: &Storage, + message_raw: vector, + signature: vector, + ctx: &mut TxContext + ) + :return: + """ + dola_protocol = load.dola_protocol_package() + + genesis = sui_project.network_config['objects']['GovernanceGenesis'] + user_manager_info = sui_project.network_config['objects']['UserManagerInfo'] + system_storage = sui_project.network_config['objects']['SystemStorage'] + + bool_global = sui_project.network_config['bool_network']['global_state'] + core_state = sui_project.network_config['bool_network']['core_state'] + + result = dola_protocol.system_core_bool_adapter.unbind_user_address.simulate( + genesis, + user_manager_info, + bool_global, + core_state, + system_storage, + list(bytes.fromhex(message_raw.replace('0x', ''))), + list(bytes.fromhex(signature.replace('0x', ''))), + ) + + gas = calculate_sui_gas(result['effects']['gasUsed']) + status = result['effects']['status']['status'] + executed = False + if relay_fee >= int(fee_rate * gas): + executed = True + result = dola_protocol.system_core_bool_adapter.unbind_user_address( + genesis, + user_manager_info, + bool_global, + core_state, + system_storage, + list(bytes.fromhex(message_raw.replace('0x', ''))), + list(bytes.fromhex(signature.replace('0x', ''))), + ) + return gas, executed, status, result['effects']['transactionDigest'] + elif status == 'failure': + return gas, executed, result['effects']['status']['error'], "" + else: + return gas, executed, status, "" + + +def parse_u16(vec): + return vec[0] + (vec[1] << 8) + + +def parse_u64(data: list): + output = 0 + for i in range(8): + output = (output << 8) + int(data[7 - i]) + return output + + +def parse_u256(data: list): + output = 0 + for i in range(32): + output = (output << 8) + int(data[31 - i]) + return output + + +def convert_vec_u16_to_list(vec): + length = vec[0] + return [parse_u16(vec[1 + i * 2: 3 + i * 2]) for i in range(length)] diff --git a/sui/scripts/dola_sui_sdk/load.py b/sui/scripts/dola_sui_sdk/load.py index 44b65acd..01dcd6e9 100644 --- a/sui/scripts/dola_sui_sdk/load.py +++ b/sui/scripts/dola_sui_sdk/load.py @@ -56,7 +56,7 @@ def wormhole_package(package_id: str = None): package_id: str = sui_project.network_config['packages']['wormhole'] return sui_package(package_id, Path.home().joinpath(Path( - ".move/https___github_com_wormhole-foundation_wormhole_git_fcfe551da0f46b704b76b09ae11dca3dd9387837/sui/wormhole"))) + ".move/https___github_com_wormhole-foundation_wormhole_git_d050ad1d67a5b7da9fb65030aad12ef5d774ccad/sui/wormhole"))) def pyth_package(): diff --git a/sui/scripts/relayer_bool.py b/sui/scripts/relayer_bool.py new file mode 100644 index 00000000..1308834b --- /dev/null +++ b/sui/scripts/relayer_bool.py @@ -0,0 +1,158 @@ +import functools +import logging +from dotenv import dotenv_values +from pathlib import Path + +from sui_brownie.parallelism import ProcessExecutor + +import dola_ethereum_sdk +from dola_ethereum_sdk import get_account +import dola_sui_sdk +from dola_sui_sdk.load import sui_project +from dola_sui_sdk.lendingBool import dispatch + +from boolnetwork import BoolWatcher, BoolExecutorETH, BoolExecutorSui + + +def get_mongodb_uri(): + env_file = sui_project.config['dotenv'] + env_values = dotenv_values(sui_project.project_path.joinpath(env_file)) + return env_values['MONGODB_URI'] + + +dola_sui_sdk.set_dola_project_path(Path("../.."), network="sui-testnet") + +bool_network_config = sui_project.network_config["bool_network"] + + +def run_watcher( + wss_url, + config, + max_block_range, + begin_num, + verify: bool = False +): + BoolWatcher( + wss_url, + get_mongodb_uri(), + config=config, + max_block_range=max_block_range, + begin_num=begin_num, + verify=verify, + ).run() + + +def run_executor_sui( + cid, + dst_network, + dispatch, +): + # path, network, account + + # dola_sui_sdk.set_dola_project_path(Path("../.."), dst_network) + + BoolExecutorSui( + cid, + dst_network, + dispatch, + get_mongodb_uri(), + "TestAccount", + interval=5 + ).run() + + +def run_executor_eth( + cid, + dst_network, + messenger +): + # path, network, account + dola_ethereum_sdk.set_dola_project_path(Path("../..")) + dola_ethereum_sdk.set_ethereum_network(dst_network) + + executor = get_account() + + BoolExecutorETH( + cid, + dst_network, + messenger, + get_mongodb_uri(), + executor, + interval=5, + ).run() + + +class ColorFormatter(logging.Formatter): + grey = '\x1b[38;21m' + green = '\x1b[92m' + yellow = '\x1b[38;5;226m' + red = '\x1b[38;5;196m' + bold_red = '\x1b[31;1m' + reset = '\x1b[0m' + + def __init__(self, fmt): + super().__init__() + self.fmt = fmt + self.FORMATS = { + logging.DEBUG: self.grey + self.fmt + self.reset, + logging.INFO: self.green + self.fmt + self.reset, + logging.WARNING: self.yellow + self.fmt + self.reset, + logging.ERROR: self.red + self.fmt + self.reset, + logging.CRITICAL: self.bold_red + self.fmt + self.reset + } + + def format(self, record): + log_fmt = self.FORMATS.get(record.levelno) + formatter = logging.Formatter(log_fmt) + return formatter.format(record) + + +def init_logger(): + FORMAT = '%(asctime)s - %(funcName)s - %(levelname)s - %(name)s: %(message)s' + logger = logging.getLogger() + logger.setLevel("INFO") + # create console handler with a higher log level + ch = logging.StreamHandler() + ch.setLevel(logging.INFO) + + ch.setFormatter(ColorFormatter(FORMAT)) + + logger.addHandler(ch) + + +def testnet(): + bool_node_url = bool_network_config["node_url"] + bool_cids = bool_network_config["cids"] + bool_sui_config = bool_network_config["chains"]["sui-testnet"] + bool_bevm_config = bool_network_config["chains"]["bevm-testnet"] + + + pt = ProcessExecutor(executor=3) + + pt.run([ + functools.partial( + run_watcher, + bool_node_url, + bool_cids, + 20, + 7298606, + True + ), + functools.partial( + run_executor_sui, + bool_sui_config["cid"], + "sui-testnet", + dispatch + ), + functools.partial( + run_executor_eth, + bool_bevm_config["cid"], + "bevm-test", + bool_bevm_config["messenger"] + ) + ]) + + +if __name__ == '__main__': + init_logger() + testnet()