瀏覽代碼

Substrate: Support contracts node v0.22 (#1080)

Change the linker to use stabilized APIs for any remaining __unstable__
Update integration tests:
-    Introduce a helper for the (currently) broken contracts query API
-    Use WeightV2 for gas limits
-    Use estimated gas from dry run instead of hardcoded value
-    Compile test contracts in parallel
Updates to the latest contracts CI image for pallet contracts v0.22.1. I'm aware that this is not ideal but it's what's available right now. There shouldn't be a breaking release soon, we can switch to production as soon as its here.
Cyrill Leutwiler 3 年之前
父節點
當前提交
46c282207f

+ 1 - 1
.github/workflows/test.yml

@@ -309,7 +309,7 @@ jobs:
       # We can't run substrate as a github actions service, since it requires
       # command line arguments. See https://github.com/actions/runner/pull/1152
     - name: Start substrate
-      run: docker run -d -p 9944:9944 paritytech/contracts-ci-linux:production substrate-contracts-node --dev --ws-external
+      run: docker run -d -p 9944:9944 paritytech/contracts-ci-linux:latest substrate-contracts-node --dev --ws-external
     - uses: actions/setup-node@v3
       with:
         node-version: '14'

+ 21 - 17
integration/substrate/UniswapV2ERC20.spec.ts

@@ -1,5 +1,5 @@
 import expect from 'expect';
-import { gasLimit, createConnection, deploy, transaction, aliceKeypair, daveKeypair } from './index';
+import { weight, createConnection, deploy, transaction, aliceKeypair, daveKeypair, query } from './index';
 import { ContractPromise } from '@polkadot/api-contract';
 import { ApiPromise } from '@polkadot/api';
 import { KeyringPair } from '@polkadot/keyring/types';
@@ -31,19 +31,19 @@ describe('Deploy UniswapV2ERC20 contract and test', () => {
     });
 
     it('name, symbol, decimals, totalSupply, balanceOf, DOMAIN_SEPARATOR, PERMIT_TYPEHASH', async () => {
-        const { output: name } = await token.query.name(alice.address, {});
+        const { output: name } = await query(conn, alice, token, "name");
         expect(name?.toJSON()).toEqual('Uniswap V2')
-        const { output: symbol } = await token.query.symbol(alice.address, {});
+        const { output: symbol } = await query(conn, alice, token, "symbol");
         expect(symbol?.toJSON()).toEqual('UNI-V2')
-        const { output: decimals } = await token.query.decimals(alice.address, {});
+        const { output: decimals } = await query(conn, alice, token, "decimals");
         expect(decimals?.toJSON()).toEqual(18)
-        const { output: totalSupply } = await token.query.totalSupply(alice.address, {});
+        const { output: totalSupply } = await query(conn, alice, token, "totalSupply");
         //console.log(`total supply: ${totalSupply?.toHuman()}`);
         expect(totalSupply?.eq(TOTAL_SUPPLY)).toBeTruthy();
-        const { output: bal } = await token.query.balanceOf(alice.address, {}, alice.address);
+        const { output: bal } = await query(conn, alice, token, "balanceOf", [alice.address]);
         expect(bal?.eq(TOTAL_SUPPLY)).toBeTruthy();
 
-        const { output: domain_seperator } = await token.query.domainSeparator(alice.address, {});
+        const { output: domain_seperator } = await query(conn, alice, token, "domainSeparator");
 
         let expected = keccakAsHex(Buffer.concat([
             keccakAsU8a('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
@@ -56,25 +56,27 @@ describe('Deploy UniswapV2ERC20 contract and test', () => {
         //console.log(`domain_separator: ${domain_seperator} ${expected}`);
         expect(domain_seperator?.eq(expected)).toBeTruthy();
 
-        const { output: permit_typehash } = await token.query.permitTypehash(alice.address, {});
+        const { output: permit_typehash } = await query(conn, alice, token, "permitTypehash");
         expect(permit_typehash?.eq('0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9')).toBeTruthy();
     })
 
     it('approve', async () => {
+        let gasLimit = await weight(conn, token, "approve", [dave.address, TEST_AMOUNT]);
         let tx = token.tx.approve({ gasLimit }, dave.address, TEST_AMOUNT);
         await transaction(tx, alice);
 
-        let { output } = await token.query.allowance(alice.address, {}, alice.address, dave.address);
+        let { output } = await query(conn, alice, token, "allowance", [alice.address, dave.address]);
         expect(output?.eq(TEST_AMOUNT)).toBeTruthy();
     })
 
     it('transfer', async () => {
+        let gasLimit = await weight(conn, token, "approve", [dave.address, TEST_AMOUNT]);
         let tx = token.tx.transfer({ gasLimit }, dave.address, TEST_AMOUNT);
         await transaction(tx, alice);
 
-        const { output: aliceBal } = await token.query.balanceOf(alice.address, {}, alice.address);
+        const { output: aliceBal } = await query(conn, alice, token, "balanceOf", [alice.address]);
         expect(aliceBal?.eq(TOTAL_SUPPLY - TEST_AMOUNT)).toBeTruthy();
-        const { output: daveBal } = await token.query.balanceOf(alice.address, {}, dave.address);
+        const { output: daveBal } = await query(conn, alice, token, "balanceOf", [dave.address]);
         expect(daveBal?.eq(TEST_AMOUNT)).toBeTruthy();
     })
 
@@ -84,32 +86,34 @@ describe('Deploy UniswapV2ERC20 contract and test', () => {
     // })
 
     it('transferFrom', async () => {
+        let gasLimit = await weight(conn, token, "approve", [dave.address, TEST_AMOUNT]);
         let tx = token.tx.approve({ gasLimit }, dave.address, TEST_AMOUNT);
         await transaction(tx, alice);
 
         tx = token.tx.transferFrom({ gasLimit }, alice.address, dave.address, TEST_AMOUNT);
         await transaction(tx, dave);
 
-        const { output: allowance } = await token.query.allowance(alice.address, {}, alice.address, dave.address);
+        const { output: allowance } = await query(conn, alice, token, "allowance", [alice.address, dave.address]);
         expect(allowance?.eq(0)).toBeTruthy();
-        const { output: aliceBal } = await token.query.balanceOf(alice.address, {}, alice.address);
+        const { output: aliceBal } = await query(conn, alice, token, "balanceOf", [alice.address]);
         expect(aliceBal?.eq(TOTAL_SUPPLY - TEST_AMOUNT)).toBeTruthy();
-        const { output: daveBal } = await token.query.balanceOf(alice.address, {}, dave.address);
+        const { output: daveBal } = await query(conn, alice, token, "balanceOf", [dave.address]);
         expect(daveBal?.eq(TEST_AMOUNT)).toBeTruthy();
     })
 
     it('transferFrom:max', async () => {
+        let gasLimit = await weight(conn, token, "approve", [dave.address, MAX_UINT256]);
         let tx = token.tx.approve({ gasLimit }, dave.address, MAX_UINT256);
         await transaction(tx, alice);
 
         tx = token.tx.transferFrom({ gasLimit }, alice.address, dave.address, TEST_AMOUNT);
         await transaction(tx, dave);
 
-        const { output: allowance } = await token.query.allowance(alice.address, {}, alice.address, dave.address);
+        const { output: allowance } = await query(conn, alice, token, "allowance", [alice.address, dave.address]);
         expect(allowance?.eq(MAX_UINT256 - TEST_AMOUNT)).toBeTruthy();
-        const { output: aliceBal } = await token.query.balanceOf(alice.address, {}, alice.address);
+        const { output: aliceBal } = await query(conn, alice, token, "balanceOf", [alice.address]);
         expect(aliceBal?.eq(TOTAL_SUPPLY - TEST_AMOUNT)).toBeTruthy();
-        const { output: daveBal } = await token.query.balanceOf(alice.address, {}, dave.address);
+        const { output: daveBal } = await query(conn, alice, token, "balanceOf", [dave.address]);
         expect(daveBal?.eq(TEST_AMOUNT)).toBeTruthy();
     })
 });

+ 16 - 13
integration/substrate/UniswapV2Factory.spec.ts

@@ -1,5 +1,5 @@
 import expect from 'expect';
-import { gasLimit, createConnection, deploy, transaction, aliceKeypair, daveKeypair } from './index';
+import { weight, createConnection, deploy, transaction, aliceKeypair, daveKeypair, query } from './index';
 import { ContractPromise } from '@polkadot/api-contract';
 import { ApiPromise } from '@polkadot/api';
 import { KeyringPair } from '@polkadot/keyring/types';
@@ -39,18 +39,19 @@ describe('UniswapV2Factory', () => {
   });
 
   it('feeTo, feeToSetter, allPairsLength', async () => {
-    const { output: feeTo } = await factory.query.feeTo(alice.address, {});
+    const { output: feeTo } = await query(conn, alice, factory, "feeTo");
     // This is the 32-byte 0-address in ss58 format
     expect(feeTo?.eq('5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM')).toBeTruthy();
 
-    const { output: feeToSetter } = await factory.query.feeToSetter(alice.address, {});
+    const { output: feeToSetter } = await query(conn, alice, factory, "feeToSetter");
     expect(feeToSetter?.eq(alice.address)).toBeTruthy();
 
-    const { output: allPairsLength } = await factory.query.allPairsLength(alice.address, {});
+    const { output: allPairsLength } = await query(conn, alice, factory, "allPairsLength");
     expect(allPairsLength?.eq(0)).toBeTruthy();
   })
 
   async function createPair(tokens: [string, string]) {
+    let gasLimit = await weight(conn, factory, "createPair", [tokens[0], tokens[1]]);
     let tx = factory.tx.createPair({ gasLimit }, ...tokens);
 
     let res0: any = await transaction(tx, alice);
@@ -64,27 +65,27 @@ describe('UniswapV2Factory', () => {
 
     let pair_address = events[0].args[2].toString();
 
-    const { output: get_pair } = await factory.query.getPair(alice.address, {}, ...tokens);
+    const { output: get_pair } = await query(conn, alice, factory, "getPair", [tokens[0], tokens[1]]);
     expect(get_pair?.eq(pair_address)).toBeTruthy();
 
-    const { output: pairRev } = await factory.query.getPair(alice.address, {}, ...tokens.slice().reverse());
+    const { output: pairRev } = await query(conn, alice, factory, "getPair", [tokens[1], tokens[0]]);
     expect(pairRev?.eq(pair_address)).toBeTruthy();
 
-    const { output: pair0 } = await factory.query.allPairs(alice.address, {}, 0);
+    const { output: pair0 } = await query(conn, alice, factory, "allPairs", [0]);
     expect(pair0?.eq(pair_address)).toBeTruthy();
 
-    const { output: pairLength } = await factory.query.allPairsLength(alice.address, {});
+    const { output: pairLength } = await query(conn, alice, factory, "allPairsLength");
     expect(pairLength?.eq(1)).toBeTruthy();
 
     const pair = new ContractPromise(conn, pairAbi, pair_address);
 
-    const { output: pair_factory } = await pair.query.factory(alice.address, {});
+    const { output: pair_factory } = await query(conn, alice, pair, "factory");
     expect(pair_factory?.eq(factory.address)).toBeTruthy();
 
-    const { output: token0 } = await pair.query.token0(alice.address, {});
+    const { output: token0 } = await query(conn, alice, pair, "token0");
     expect(token0?.eq(TEST_ADDRESSES[0])).toBeTruthy();
 
-    const { output: token1 } = await pair.query.token1(alice.address, {});
+    const { output: token1 } = await query(conn, alice, pair, "token1");
     expect(token1?.eq(TEST_ADDRESSES[1])).toBeTruthy();
   }
 
@@ -97,18 +98,20 @@ describe('UniswapV2Factory', () => {
   })
 
   it('setFeeTo', async () => {
+    let gasLimit = await weight(conn, factory, "setFeeTo", [dave.address]);
     let tx = factory.tx.setFeeTo({ gasLimit }, dave.address);
     await transaction(tx, alice);
 
-    const { output: feeTo } = await factory.query.feeTo(alice.address, {});
+    const { output: feeTo } = await query(conn, alice, factory, "feeTo");
     expect(feeTo?.eq(dave.address)).toBeTruthy();
   })
 
   it('setFeeToSetter', async () => {
+    let gasLimit = await weight(conn, factory, "setFeeToSetter", [dave.address]);
     let tx = factory.tx.setFeeToSetter({ gasLimit }, dave.address);
     await transaction(tx, alice);
 
-    const { output: feeTo } = await factory.query.feeToSetter(alice.address, {});
+    const { output: feeTo } = await query(conn, alice, factory, "feeToSetter");
     expect(feeTo?.eq(dave.address)).toBeTruthy();
   })
 })

+ 43 - 32
integration/substrate/UniswapV2Pair.spec.ts

@@ -1,9 +1,8 @@
 import expect from 'expect';
-import { gasLimit, createConnection, deploy, transaction, aliceKeypair, daveKeypair } from './index';
+import { weight, query, createConnection, deploy, transaction, aliceKeypair, daveKeypair } from './index';
 import { ContractPromise } from '@polkadot/api-contract';
 import { ApiPromise } from '@polkadot/api';
 import { KeyringPair } from '@polkadot/keyring/types';
-import type { Codec } from '@polkadot/types/types';
 
 const MINIMUM_LIQUIDITY = BigInt(1000);
 const TOTAL_SUPPLY = BigInt(10000e18);
@@ -41,15 +40,16 @@ describe('UniswapV2Pair', () => {
 
         const tokenB = new ContractPromise(conn, tokenB_contract.abi, tokenB_contract.address);
 
+        let gasLimit = await weight(conn, factory, "createPair", [tokenA.address, tokenB.address]);
         let tx = factory.tx.createPair({ gasLimit }, tokenA.address, tokenB.address);
 
         await transaction(tx, alice);
 
-        const { output: get_pair } = await factory.query.getPair(alice.address, {}, tokenA.address, tokenB.address);
+        const { output: get_pair } = await query(conn, alice, factory, "getPair", [tokenA.address, tokenB.address]);
 
         pair = new ContractPromise(conn, pairAbi, get_pair!.toString());
 
-        const { output: token0_address } = await pair.query.token0(alice.address, {});
+        const { output: token0_address } = await query(conn, alice, pair, "token0");
 
         if (tokenA.address.toString() == token0_address!.toString()) {
             token0 = tokenA;
@@ -69,24 +69,27 @@ describe('UniswapV2Pair', () => {
         const token0Amount = BigInt(1e18)
         const token1Amount = BigInt(4e18)
 
+        let gasLimit = await weight(conn, token0, "transfer", [pair.address, token0Amount]);
         let tx = token0.tx.transfer({ gasLimit }, pair.address, token0Amount);
         await transaction(tx, alice);
+        gasLimit = await weight(conn, token1, "transfer", [pair.address, token1Amount]);
         tx = token1.tx.transfer({ gasLimit }, pair.address, token1Amount);
         await transaction(tx, alice);
         const expectedLiquidity = BigInt(2e18)
 
+        gasLimit = await weight(conn, pair, "mint", [alice.address]);
         tx = pair.tx.mint({ gasLimit }, alice.address);
         await transaction(tx, alice);
 
-        const { output: totalSupply } = await pair.query.totalSupply(alice.address, {});
+        const { output: totalSupply } = await query(conn, alice, pair, "totalSupply");
         expect(totalSupply?.eq(expectedLiquidity)).toBeTruthy();
-        const { output: bal } = await pair.query.balanceOfAddress(alice.address, {}, alice.address);
+        const { output: bal } = await query(conn, alice, pair, "balanceOfAddress", [alice.address]);
         expect(bal?.eq(expectedLiquidity - MINIMUM_LIQUIDITY)).toBeTruthy();
-        const { output: bal0 } = await token0.query.balanceOf(alice.address, {}, pair.address);
+        const { output: bal0 } = await query(conn, alice, token0, "balanceOf", [pair.address]);
         expect(bal0?.eq(token0Amount)).toBeTruthy();
-        const { output: bal1 } = await token1.query.balanceOf(alice.address, {}, pair.address);
+        const { output: bal1 } = await query(conn, alice, token1, "balanceOf", [pair.address]);
         expect(bal1?.eq(token1Amount)).toBeTruthy();
-        const { output: reserves } = await pair.query.getReserves(alice.address, {});
+        const { output: reserves } = await query(conn, alice, pair, "getReserves");
         // surely there must be a better way.
         let v: any = reserves;
         expect(v[0].eq(token0Amount)).toBeTruthy();
@@ -94,11 +97,13 @@ describe('UniswapV2Pair', () => {
     })
 
     async function addLiquidity(token0Amount: BigInt, token1Amount: BigInt) {
+        let gasLimit = await weight(conn, token0, "transfer", [pair.address, token0Amount]);
         let tx = token0.tx.transfer({ gasLimit }, pair.address, token0Amount);
         await transaction(tx, alice);
+        gasLimit = await weight(conn, token1, "transfer", [pair.address, token1Amount]);
         tx = token1.tx.transfer({ gasLimit }, pair.address, token1Amount);
         await transaction(tx, alice);
-
+        gasLimit = await weight(conn, pair, "mint", [alice.address]);
         tx = pair.tx.mint({ gasLimit }, alice.address);
         await transaction(tx, alice);
     }
@@ -111,27 +116,29 @@ describe('UniswapV2Pair', () => {
         const swapAmount = BigInt(1e18)
         const expectedOutputAmount = BigInt(1662497915624478906)
 
+        let gasLimit = await weight(conn, token0, "transfer", [pair.address, swapAmount]);
         let tx = token0.tx.transfer({ gasLimit }, pair.address, swapAmount);
         await transaction(tx, alice);
 
+        gasLimit = await weight(conn, pair, "swap", [0, expectedOutputAmount, alice.address, '']);
         tx = pair.tx.swap({ gasLimit }, 0, expectedOutputAmount, alice.address, '');
         await transaction(tx, alice);
 
-        const { output: reserves } = await pair.query.getReserves(alice.address, {});
+        const { output: reserves } = await query(conn, alice, pair, "getReserves");
         // surely there must be a better way.
         let v: any = reserves;
         expect(v[0].eq(token0Amount + swapAmount)).toBeTruthy();
         expect(v[1].eq(token1Amount - expectedOutputAmount)).toBeTruthy();
 
-        const { output: bal0 } = await token0.query.balanceOf(alice.address, {}, pair.address);
+        const { output: bal0 } = await query(conn, alice, token0, "balanceOf", [pair.address]);
         expect(bal0?.eq(token0Amount + swapAmount)).toBeTruthy();
-        const { output: bal1 } = await token1.query.balanceOf(alice.address, {}, pair.address);
+        const { output: bal1 } = await query(conn, alice, token1, "balanceOf", [pair.address]);
         expect(bal1?.eq(token1Amount - expectedOutputAmount)).toBeTruthy();
 
-        const { output: returnTotalSupplyToken0 } = await token0.query.totalSupply(alice.address, {});
-        const { output: returnTotalSupplyToken1 } = await token1.query.totalSupply(alice.address, {});
-        const { output: walletBal0 } = await token0.query.balanceOf(alice.address, {}, alice.address);
-        const { output: walletBal1 } = await token1.query.balanceOf(alice.address, {}, alice.address);
+        const { output: returnTotalSupplyToken0 } = await query(conn, alice, token0, "totalSupply");
+        const { output: returnTotalSupplyToken1 } = await query(conn, alice, token1, "totalSupply");
+        const { output: walletBal0 } = await query(conn, alice, token0, "balanceOf", [alice.address]);
+        const { output: walletBal1 } = await query(conn, alice, token1, "balanceOf", [alice.address]);
 
         const totalSupplyToken0 = BigInt(returnTotalSupplyToken0!.toString());
         const totalSupplyToken1 = BigInt(returnTotalSupplyToken1!.toString());
@@ -148,27 +155,29 @@ describe('UniswapV2Pair', () => {
         const swapAmount = BigInt(1e18)
         const expectedOutputAmount = BigInt(453305446940074565)
 
+        let gasLimit = await weight(conn, token1, "transfer", [pair.address, swapAmount]);
         let tx = token1.tx.transfer({ gasLimit }, pair.address, swapAmount);
         await transaction(tx, alice);
 
+        gasLimit = await weight(conn, pair, "swap", [expectedOutputAmount, 0, alice.address, '']);
         tx = pair.tx.swap({ gasLimit }, expectedOutputAmount, 0, alice.address, '');
         await transaction(tx, alice);
 
-        const { output: reserves } = await pair.query.getReserves(alice.address, {});
+        const { output: reserves } = await query(conn, alice, pair, "getReserves");
         // surely there must be a better way.
         let v: any = reserves;
         expect(v[0].eq(token0Amount - expectedOutputAmount)).toBeTruthy();
         expect(v[1].eq(token1Amount + swapAmount)).toBeTruthy();
 
-        const { output: bal0 } = await token0.query.balanceOf(alice.address, {}, pair.address);
+        const { output: bal0 } = await query(conn, alice, token0, "balanceOf", [pair.address]);
         expect(bal0?.eq(token0Amount - expectedOutputAmount)).toBeTruthy();
-        const { output: bal1 } = await token1.query.balanceOf(alice.address, {}, pair.address);
+        const { output: bal1 } = await query(conn, alice, token1, "balanceOf", [pair.address]);
         expect(bal1?.eq(token1Amount + swapAmount)).toBeTruthy();
 
-        const { output: returnTotalSupplyToken0 } = await token0.query.totalSupply(alice.address, {});
-        const { output: returnTotalSupplyToken1 } = await token1.query.totalSupply(alice.address, {});
-        const { output: walletBal0 } = await token0.query.balanceOf(alice.address, {}, alice.address);
-        const { output: walletBal1 } = await token1.query.balanceOf(alice.address, {}, alice.address);
+        const { output: returnTotalSupplyToken0 } = await query(conn, alice, token0, "totalSupply");
+        const { output: returnTotalSupplyToken1 } = await query(conn, alice, token1, "totalSupply");
+        const { output: walletBal0 } = await query(conn, alice, token0, "balanceOf", [alice.address]);
+        const { output: walletBal1 } = await query(conn, alice, token1, "balanceOf", [alice.address]);
 
         const totalSupplyToken0 = BigInt(returnTotalSupplyToken0!.toString());
         const totalSupplyToken1 = BigInt(returnTotalSupplyToken1!.toString());
@@ -184,32 +193,34 @@ describe('UniswapV2Pair', () => {
 
         const expectedLiquidity = BigInt(3e18)
 
+        let gasLimit = await weight(conn, pair, "transferAddressUint256", [pair.address, expectedLiquidity - MINIMUM_LIQUIDITY]);
         let tx = pair.tx.transferAddressUint256({ gasLimit }, pair.address, expectedLiquidity - MINIMUM_LIQUIDITY);
         await transaction(tx, alice);
 
+        gasLimit = await weight(conn, pair, "burn", [alice.address]);
         tx = pair.tx.burn({ gasLimit }, alice.address);
         await transaction(tx, alice);
 
-        const { output: walletBal0 } = await pair.query.balanceOfAddress(alice.address, {}, alice.address);
+        const { output: walletBal0 } = await query(conn, alice, pair, "balanceOfAddress", [alice.address]);
         expect(walletBal0?.eq(0)).toBeTruthy();
 
-        const { output: pairTotalSupply } = await pair.query.totalSupply(alice.address, {});
+        const { output: pairTotalSupply } = await query(conn, alice, pair, "totalSupply");
         expect(pairTotalSupply?.eq(MINIMUM_LIQUIDITY)).toBeTruthy();
 
-        const { output: token0pairBal } = await token0.query.balanceOf(alice.address, {}, pair.address);
+        const { output: token0pairBal } = await query(conn, alice, token0, "balanceOf", [pair.address]);
         expect(token0pairBal?.eq(1000)).toBeTruthy();
-        const { output: token1pairBal } = await token1.query.balanceOf(alice.address, {}, pair.address);
+        const { output: token1pairBal } = await query(conn, alice, token1, "balanceOf", [pair.address]);
         expect(token1pairBal?.eq(1000)).toBeTruthy();
 
-        const { output: retToken0TotalSupply } = await token0.query.totalSupply(alice.address, {});
-        const { output: retToken1TotalSupply } = await token1.query.totalSupply(alice.address, {});
+        const { output: retToken0TotalSupply } = await query(conn, alice, token0, "totalSupply");
+        const { output: retToken1TotalSupply } = await query(conn, alice, token1, "totalSupply");
 
         const totalSupplyToken0 = BigInt(retToken0TotalSupply!.toString());
         const totalSupplyToken1 = BigInt(retToken1TotalSupply!.toString());
 
-        const { output: bal0 } = await token0.query.balanceOf(alice.address, {}, alice.address);
+        const { output: bal0 } = await query(conn, alice, token0, "balanceOf", [alice.address]);
         expect(bal0?.eq(totalSupplyToken0 - 1000n)).toBeTruthy();
-        const { output: bal1 } = await token1.query.balanceOf(alice.address, {}, alice.address);
+        const { output: bal1 } = await query(conn, alice, token1, "balanceOf", [alice.address]);
         expect(bal1?.eq(totalSupplyToken1 - 1000n)).toBeTruthy();
     })
 });

+ 5 - 4
integration/substrate/array_struct_mapping_storage.spec.ts

@@ -1,5 +1,5 @@
 import expect from 'expect';
-import { gasLimit, createConnection, deploy, aliceKeypair, transaction } from './index';
+import { weight, createConnection, deploy, aliceKeypair, transaction, query } from './index';
 import { ContractPromise } from '@polkadot/api-contract';
 
 describe('Deploy array_struct_mapping_storage contract and test', () => {
@@ -14,6 +14,7 @@ describe('Deploy array_struct_mapping_storage contract and test', () => {
         let contract = new ContractPromise(conn, deployed_contract.abi, deployed_contract.address);
 
         // first set a canary
+        let gasLimit = await weight(conn, contract, "setNumber", [2147483647]);
         let tx = contract.tx.setNumber({ gasLimit }, 2147483647);
 
         await transaction(tx, alice);
@@ -39,7 +40,7 @@ describe('Deploy array_struct_mapping_storage contract and test', () => {
         // test our values
         for (let array_no = 0; array_no < 2; array_no += 1) {
             for (let i = 0; i < 10; i += 1) {
-                let { output } = await contract.query.get(alice.address, {}, array_no, 102 + i + array_no * 500);
+                let { output } = await query(conn, alice, contract, "get", [array_no, 102 + i + array_no * 500]);
 
                 let number = Number.parseInt(output!.toString());
 
@@ -53,7 +54,7 @@ describe('Deploy array_struct_mapping_storage contract and test', () => {
         await transaction(tx, alice);
 
         for (let i = 0; i < 10; i += 1) {
-            let { output } = await contract.query.get(alice.address, {}, 0, 102 + i);
+            let { output } = await query(conn, alice, contract, "get", [0, 102 + i]);
 
             let number = Number.parseInt(output!.toString());
 
@@ -65,7 +66,7 @@ describe('Deploy array_struct_mapping_storage contract and test', () => {
         }
 
         // test our canary
-        let { output } = await contract.query.number(alice.address, {});
+        let { output } = await query(conn, alice, contract, "number");
 
         let number = Number.parseInt(output!.toString());
 

+ 7 - 5
integration/substrate/arrays.spec.ts

@@ -1,6 +1,6 @@
 import expect from 'expect';
 import crypto from 'crypto';
-import { gasLimit, createConnection, deploy, transaction, aliceKeypair, } from './index';
+import { createConnection, deploy, transaction, aliceKeypair, weight, query, } from './index';
 import { ContractPromise } from '@polkadot/api-contract';
 import { ApiPromise } from '@polkadot/api';
 
@@ -38,6 +38,7 @@ describe('Deploy arrays contract and test', () => {
                 perms.push(`Perm${p + 1}`);
             }
 
+            let gasLimit = await weight(conn, contract, "addUser", [id, addr, name, perms]);
             const tx1 = contract.tx.addUser({ gasLimit }, id, addr, name, perms);
 
             await transaction(tx1, alice);
@@ -47,7 +48,7 @@ describe('Deploy arrays contract and test', () => {
 
         let user = users[Math.floor(Math.random() * users.length)];
 
-        let res1 = await contract.query.getUserById(alice.address, {}, user.id);
+        let res1 = await query(conn, alice, contract, "getUserById", [user.id]);
 
         expect(res1.output?.toJSON()).toStrictEqual(user);
 
@@ -56,22 +57,23 @@ describe('Deploy arrays contract and test', () => {
 
             let p = perms[Math.floor(Math.random() * perms.length)];
 
-            let res2 = await contract.query.hasPermission(alice.address, {}, user.id, p);
+            let res2 = await query(conn, alice, contract, "hasPermission", [user.id, p]);
 
             expect(res2.output?.toJSON()).toBe(true);
         }
 
         user = users[Math.floor(Math.random() * users.length)];
 
-        let res3 = await contract.query.getUserByAddress(alice.address, {}, user.addr);
+        let res3 = await query(conn, alice, contract, "getUserByAddress", [user.addr]);
 
         expect(res3.output?.toJSON()).toStrictEqual(user);
 
+        let gasLimit = await weight(conn, contract, "removeUser", [user.id]);
         const tx2 = contract.tx.removeUser({ gasLimit }, user.id);
 
         await transaction(tx2, alice);
 
-        let res4 = await contract.query.userExists(alice.address, {}, user.id);
+        let res4 = await query(conn, alice, contract, "userExists", [user.id]);
 
         expect(res4.output?.toJSON()).toBe(false);
     });

+ 5 - 4
integration/substrate/asserts.spec.ts

@@ -1,5 +1,5 @@
 import expect from 'expect';
-import { gasLimit, createConnection, deploy, transaction, aliceKeypair, } from './index';
+import { weight, createConnection, deploy, transaction, aliceKeypair, query, } from './index';
 import { ContractPromise } from '@polkadot/api-contract';
 import { ApiPromise } from '@polkadot/api';
 
@@ -24,13 +24,14 @@ describe('Deploy asserts contract and test', () => {
 
         let contract = new ContractPromise(conn, deploy_contract.abi, deploy_contract.address);
 
-        let res0 = await contract.query.var(alice.address, {});
+        let res0 = await query(conn, alice, contract, "var");
 
         expect(res0.output?.toJSON()).toEqual(1);
 
-        let res1 = await contract.query.testAssertRpc(alice.address, {});
+        let res1 = await query(conn, alice, contract, "testAssertRpc");
         expect(res1.result.toHuman()).toEqual({ "Err": { "Module": { "error": "0x0b000000", "index": "7" } } });
 
+        let gasLimit = await weight(conn, contract, "testAssert");
         let tx = contract.tx.testAssert({ gasLimit });
 
         let res2 = await transaction(tx, alice).then(() => {
@@ -39,7 +40,7 @@ describe('Deploy asserts contract and test', () => {
 
         expect(res2.dispatchError.toHuman()).toEqual({ "Module": { "error": "0x0b000000", "index": "7" } });
 
-        let res3 = await contract.query.var(alice.address, {});
+        let res3 = await query(conn, alice, contract, "var");
 
         expect(res3.output?.toJSON()).toEqual(1);
     });

+ 3 - 2
integration/substrate/balances.spec.ts

@@ -1,5 +1,5 @@
 import expect from 'expect';
-import { gasLimit, createConnection, deploy, transaction, aliceKeypair, daveKeypair } from './index';
+import { weight, createConnection, deploy, transaction, aliceKeypair, daveKeypair, query } from './index';
 import { ContractPromise } from '@polkadot/api-contract';
 import { ApiPromise } from '@polkadot/api';
 
@@ -25,11 +25,12 @@ describe('Deploy balances contract and test', () => {
 
         let contract = new ContractPromise(conn, deploy_contract.abi, deploy_contract.address);
 
-        let { output: contractRpcBal } = await contract.query.getBalance(alice.address, {});
+        let { output: contractRpcBal } = await query(conn, alice, contract, "getBalance");
         let { data: { free: contractQueryBalBefore } } = await conn.query.system.account(String(deploy_contract.address));
 
         expect(contractRpcBal?.toString()).toBe(contractQueryBalBefore.toString());
 
+        let gasLimit = await weight(conn, contract, "payMe");
         let tx = contract.tx.payMe({ gasLimit, value: 1000000n });
 
         await transaction(tx, alice);

+ 5 - 5
integration/substrate/builtins.spec.ts

@@ -1,5 +1,5 @@
 import expect from 'expect';
-import { gasLimit, createConnection, deploy, transaction, aliceKeypair, } from './index';
+import { createConnection, deploy, transaction, aliceKeypair, query, } from './index';
 import { ContractPromise } from '@polkadot/api-contract';
 
 describe('Deploy builtin contract and test', () => {
@@ -14,19 +14,19 @@ describe('Deploy builtin contract and test', () => {
         let contract = new ContractPromise(conn, deployed_contract.abi, deployed_contract.address);
 
         // call the constructor
-        let ripemd160_res = await contract.query.hashRipemd160(alice.address, {}, '0x' + Buffer.from('Call me Ishmael.', 'utf8').toString('hex'));
+        let ripemd160_res = await query(conn, alice, contract, "hashRipemd160", ['0x' + Buffer.from('Call me Ishmael.', 'utf8').toString('hex')]);
 
         expect(ripemd160_res.output?.toJSON()).toBe("0x0c8b641c461e3c7abbdabd7f12a8905ee480dadf");
 
-        let sha256_res = await contract.query.hashSha256(alice.address, {}, '0x' + Buffer.from('Call me Ishmael.', 'utf8').toString('hex'));
+        let sha256_res = await query(conn, alice, contract, "hashSha256", ['0x' + Buffer.from('Call me Ishmael.', 'utf8').toString('hex')]);
 
         expect(sha256_res.output?.toJSON()).toBe("0x458f3ceeeec730139693560ecf66c9c22d9c7bc7dcb0599e8e10b667dfeac043");
 
-        let keccak256_res = await contract.query.hashKecccak256(alice.address, {}, '0x' + Buffer.from('Call me Ishmael.', 'utf8').toString('hex'));
+        let keccak256_res = await query(conn, alice, contract, "hashKecccak256", ['0x' + Buffer.from('Call me Ishmael.', 'utf8').toString('hex')]);
 
         expect(keccak256_res.output?.toJSON()).toBe("0x823ad8e1757b879aac338f9a18542928c668e479b37e4a56f024016215c5928c");
 
-        let timestamps_res = await contract.query.mrNow(alice.address, {});
+        let timestamps_res = await query(conn, alice, contract, "mrNow");
 
         let now = Math.floor(+new Date() / 1000);
 

+ 13 - 10
integration/substrate/builtins2.spec.ts

@@ -1,6 +1,7 @@
 import expect from 'expect';
-import { createConnection, deploy, aliceKeypair, gasLimit } from './index';
+import { createConnection, deploy, aliceKeypair, weight, query } from './index';
 import { ContractPromise } from '@polkadot/api-contract';
+import { convertWeight } from '@polkadot/api-contract/base/util';
 
 describe('Deploy builtins2 contract and test', () => {
     it('builtins2', async function () {
@@ -13,15 +14,17 @@ describe('Deploy builtins2 contract and test', () => {
 
         let contract = new ContractPromise(conn, deployed_contract.abi, deployed_contract.address);
 
-        let { output: blake2_128 } = await contract.query.hashBlake2128(alice.address, {}, '0x' + Buffer.from('Call me Ishmael.', 'utf8').toString('hex'));
+        let gasLimit = await weight(conn, contract, "burnGas", [0]);
+
+        let { output: blake2_128 } = await query(conn, alice, contract, "hashBlake2128", ['0x' + Buffer.from('Call me Ishmael.', 'utf8').toString('hex')]);
 
         expect(blake2_128?.toJSON()).toBe("0x56691483d63cac66c38c168c703c6f13");
 
-        let { output: blake2_256 } = await contract.query.hashBlake2256(alice.address, {}, '0x' + Buffer.from('Call me Ishmael.', 'utf8').toString('hex'));
+        let { output: blake2_256 } = await query(conn, alice, contract, "hashBlake2256", ['0x' + Buffer.from('Call me Ishmael.', 'utf8').toString('hex')]);
 
         expect(blake2_256?.toJSON()).toBe("0x1abd7330c92d835b5084219aedba821c3a599d039d5b66fb5a22ee8e813951a8");
 
-        let { output: _contract_block_number } = await contract.query.blockHeight(alice.address, {});
+        let { output: _contract_block_number } = await query(conn, alice, contract, "blockHeight", []);
 
         let contract_block_number = Number.parseInt(_contract_block_number!.toString());
 
@@ -29,19 +32,19 @@ describe('Deploy builtins2 contract and test', () => {
 
         expect(Math.abs(contract_block_number - rpc_block_number)).toBeLessThanOrEqual(3);
 
-        let { output: gas_left } = await contract.query.burnGas(alice.address, { gasLimit }, 0);
+        let { output: gas_left } = await query(conn, alice, contract, "burnGas", [0], undefined, convertWeight(gasLimit).v2Weight);
         let gas = BigInt(gas_left!.toString());
-        expect(gasLimit).toBeGreaterThan(gas);
-        let previous_diff = gasLimit - gas;
+        expect(gasLimit.toJSON().refTime).toBeGreaterThan(gas);
+        let previous_diff = BigInt(gasLimit.toJSON().refTime) - gas;
 
         // Gas metering is based on execution time:
         // Expect each call to burn between 10000..1000000 more gas than the previous iteration.
         for (let i = 1; i < 100; i++) {
-            let { output: gas_left } = await contract.query.burnGas(alice.address, { gasLimit }, i);
+            let { output: gas_left } = await query(conn, alice, contract, "burnGas", [i], undefined, convertWeight(gasLimit).v2Weight);
             let gas = BigInt(gas_left!.toString());
-            expect(gasLimit).toBeGreaterThan(gas);
+            expect(gasLimit.toJSON().refTime).toBeGreaterThan(gas);
 
-            let diff = gasLimit - gas;
+            let diff = BigInt(gasLimit.toJSON().refTime) - gas;
             expect(diff).toBeGreaterThan(previous_diff);
             expect(diff - previous_diff).toBeLessThan(1e6);
             expect(diff - previous_diff).toBeGreaterThan(1e4);

+ 4 - 3
integration/substrate/create_contract.spec.ts

@@ -1,5 +1,5 @@
 import expect from 'expect';
-import { gasLimit, createConnection, deploy, transaction, aliceKeypair, } from './index';
+import { weight, createConnection, deploy, transaction, aliceKeypair, query, } from './index';
 import { ContractPromise } from '@polkadot/api-contract';
 import { ApiPromise } from '@polkadot/api';
 
@@ -27,16 +27,17 @@ describe('Deploy create_contract contract and test', () => {
 
         let contract = new ContractPromise(conn, deploy_contract.abi, deploy_contract.address);
 
+        let gasLimit = await weight(conn, contract, "createChild");
         let tx = contract.tx.createChild({ gasLimit });
 
         await transaction(tx, alice);
 
-        let res2 = await contract.query.callChild(alice.address, {});
+        let res2 = await query(conn, alice, contract, "callChild");
 
         expect(res2.output?.toJSON()).toStrictEqual("child");
 
         // child was created with a balance of 1e15, verify
-        res2 = await contract.query.c(alice.address, {});
+        res2 = await query(conn, alice, contract, "c");
 
         let child = res2.output!.toString();
 

+ 3 - 2
integration/substrate/destruct.spec.ts

@@ -1,5 +1,5 @@
 import expect from 'expect';
-import { gasLimit, createConnection, deploy, transaction, aliceKeypair, daveKeypair } from './index';
+import { weight, createConnection, deploy, transaction, aliceKeypair, daveKeypair, query } from './index';
 import { ContractPromise } from '@polkadot/api-contract';
 import { ApiPromise } from '@polkadot/api';
 
@@ -25,13 +25,14 @@ describe('Deploy destruct contract and test', () => {
 
         let contract = new ContractPromise(conn, deploy_contract.abi, deploy_contract.address);
 
-        let hello = await contract.query.hello(alice.address, {});
+        let hello = await query(conn, alice, contract, "hello");
 
         expect(hello.output?.toJSON()).toBe('Hello');
 
         let { data: { free: daveBalBefore } } = await conn.query.system.account(dave.address);
         let { data: { free: contractBalBefore } } = await conn.query.system.account(String(deploy_contract.address));
 
+        let gasLimit = await weight(conn, contract, "selfterminate", [dave.address]);
         let tx = contract.tx.selfterminate({ gasLimit }, dave.address);
 
         await transaction(tx, alice);

+ 2 - 1
integration/substrate/events.spec.ts

@@ -1,5 +1,5 @@
 import expect from 'expect';
-import { gasLimit, createConnection, deploy, transaction, aliceKeypair, } from './index';
+import { weight, createConnection, deploy, transaction, aliceKeypair, } from './index';
 import { ContractPromise } from '@polkadot/api-contract';
 import { ApiPromise } from '@polkadot/api';
 import { DecodedEvent } from '@polkadot/api-contract/types';
@@ -22,6 +22,7 @@ describe('Deploy events contract and test event data, docs and topics', () => {
 
         let deploy_contract = await deploy(conn, alice, 'Events.contract', BigInt(0));
         let contract = new ContractPromise(conn, deploy_contract.abi, deploy_contract.address);
+        let gasLimit = await weight(conn, contract, "emitEvent");
         let tx = contract.tx.emitEvent({ gasLimit });
         let res0: any = await transaction(tx, alice);
         let events: DecodedEvent[] = res0.contractEvents;

+ 9 - 7
integration/substrate/external_call.spec.ts

@@ -1,5 +1,5 @@
 import expect from 'expect';
-import { gasLimit, createConnection, deploy, transaction, aliceKeypair, } from './index';
+import { weight, createConnection, deploy, transaction, aliceKeypair, query, } from './index';
 import { ContractPromise } from '@polkadot/api-contract';
 import { ApiPromise } from '@polkadot/api';
 
@@ -32,35 +32,37 @@ describe('Deploy external_call contract and test', () => {
 
         let callee2_contract = new ContractPromise(conn, callee2_res.abi, callee2_res.address);
 
+        let gasLimit = await weight(conn, callee_contract, "setX", [102]);
         let tx1 = callee_contract.tx.setX({ gasLimit }, 102);
 
         await transaction(tx1, alice);
 
-        let res1 = await callee_contract.query.getX(alice.address, {});
+        let res1 = await query(conn, alice, callee_contract, "getX");
 
         expect(res1.output?.toJSON()).toStrictEqual(102);
 
-        let res2 = await caller_contract.query.whoAmI(alice.address, {});
+        let res2 = await query(conn, alice, caller_contract, "whoAmI");
 
         expect(res2.output?.toString()).toEqual(caller_res.address.toString());
 
+        gasLimit = await weight(conn, caller_contract, "doCall", [callee_contract.address, 13123]);
         let tx2 = caller_contract.tx.doCall({ gasLimit }, callee_contract.address, 13123);
 
         await transaction(tx2, alice);
 
-        let res3 = await callee_contract.query.getX(alice.address, {});
+        let res3 = await query(conn, alice, callee_contract, "getX");
 
         expect(res3.output?.toJSON()).toStrictEqual(13123);
 
-        let res4 = await caller_contract.query.doCall2(alice.address, {}, callee_contract.address, 20000);
+        let res4 = await query(conn, alice, caller_contract, "doCall2", [callee_contract.address, 20000]);
 
         expect(res4.output?.toJSON()).toStrictEqual(33123);
 
-        let res5 = await caller_contract.query.doCall3(alice.address, {}, callee_contract.address, callee2_contract.address, [3, 5, 7, 9], "yo");
+        let res5 = await query(conn, alice, caller_contract, "doCall3", [callee_contract.address, callee2_contract.address, [3, 5, 7, 9], "yo"]);
 
         expect(res5.output?.toJSON()).toEqual([24, "my name is callee"]);
 
-        let res6 = await caller_contract.query.doCall4(alice.address, {}, callee_contract.address, callee2_contract.address, [1, 2, 3, 4], "asda");
+        let res6 = await query(conn, alice, caller_contract, "doCall4", [callee_contract.address, callee2_contract.address, [1, 2, 3, 4], "asda"]);
 
         expect(res6.output?.toJSON()).toEqual([10, "x:asda"]);
     });

+ 4 - 3
integration/substrate/flipper.spec.ts

@@ -1,5 +1,5 @@
 import expect from 'expect';
-import { gasLimit, createConnection, deploy, transaction, aliceKeypair, } from './index';
+import { weight, createConnection, deploy, transaction, aliceKeypair, query, } from './index';
 import { ContractPromise } from '@polkadot/api-contract';
 
 describe('Deploy flipper contract and test', () => {
@@ -13,15 +13,16 @@ describe('Deploy flipper contract and test', () => {
 
         let contract = new ContractPromise(conn, deployed_contract.abi, deployed_contract.address);
 
-        let init_value = await contract.query.get(alice.address, {});
+        let init_value = await query(conn, alice, contract, "get");
 
         expect(init_value.output?.toJSON()).toBe(true);
 
+        let gasLimit = await weight(conn, contract, "flip");
         const tx = contract.tx.flip({ gasLimit });
 
         await transaction(tx, alice);
 
-        let flipped_value = await contract.query.get(alice.address, {});
+        let flipped_value = await query(conn, alice, contract, "get");
 
         expect(flipped_value.output?.toJSON()).toBe(false);
 

+ 44 - 8
integration/substrate/index.ts

@@ -1,13 +1,15 @@
 import '@polkadot/api-augment';
 import fs, { PathLike } from 'fs';
 import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';
-import { CodePromise } from '@polkadot/api-contract';
+import { convertWeight } from '@polkadot/api-contract/base/util';
+import { CodePromise, ContractPromise } from '@polkadot/api-contract';
 import { SubmittableExtrinsic } from '@polkadot/api/types';
-import { ISubmittableResult } from '@polkadot/types/types';
+import { Codec, ISubmittableResult } from '@polkadot/types/types';
 import { KeyringPair } from '@polkadot/keyring/types';
+import expect from 'expect';
+import { ContractExecResultResult, WeightV2 } from '@polkadot/types/interfaces';
 
-const default_url: string = "ws://localhost:9944";
-export const gasLimit: bigint = 200000n * 1000000n;
+const default_url = "ws://127.0.0.1:9944";
 
 export function aliceKeypair(): KeyringPair {
   const keyring = new Keyring({ type: 'sr25519' });
@@ -19,17 +21,18 @@ export function daveKeypair(): KeyringPair {
   return keyring.addFromUri('//Dave');
 }
 
-export async function createConnection(): Promise<ApiPromise> {
-  let url = process.env.RPC_URL || default_url;
+export function createConnection(): Promise<ApiPromise> {
+  const url = process.env.RPC_URL || default_url;
 
   return ApiPromise.create({ provider: new WsProvider(url) });
 }
 
-export async function deploy(api: ApiPromise, pair: KeyringPair, file: PathLike, value: bigint, ...params: unknown[]): Promise<any> {
+export function deploy(api: ApiPromise, pair: KeyringPair, file: PathLike, value: bigint, ...params: unknown[]): Promise<any> {
   const contractJson = fs.readFileSync(file, { encoding: 'utf-8' });
 
   const code = new CodePromise(api, contractJson, null);
 
+  const gasLimit = api.registry.createType('WeightV2', convertWeight(200000n * 1000000n).v2Weight);
   const tx = code.tx.new({ gasLimit, value }, ...params);
 
   return new Promise(async (resolve, reject) => {
@@ -53,7 +56,7 @@ export async function deploy(api: ApiPromise, pair: KeyringPair, file: PathLike,
   });
 }
 
-export async function transaction(tx: SubmittableExtrinsic<"promise", ISubmittableResult>, pair: KeyringPair): Promise<ISubmittableResult> {
+export function transaction(tx: SubmittableExtrinsic<"promise", ISubmittableResult>, pair: KeyringPair): Promise<ISubmittableResult> {
   return new Promise(async (resolve, reject) => {
     const unsub = await tx.signAndSend(pair, (result: any) => {
       if (result.dispatchError) {
@@ -75,3 +78,36 @@ export async function transaction(tx: SubmittableExtrinsic<"promise", ISubmittab
     });
   });
 }
+
+// Returns the required gas estimated from a dry run
+export async function weight(api: ApiPromise, contract: ContractPromise, message: string, args?: unknown[], value?: number) {
+  const ALICE = new Keyring({ type: 'sr25519' }).addFromUri('//Alice').address;
+  const msg = contract.abi.findMessage(message);
+  const dry = await api.call.contractsApi.call(ALICE, contract.address, value ? value : 0, null, null, msg.toU8a(args ? args : []));
+  return dry.gasRequired;
+}
+
+// FIXME: The old contract.query API does not support WeightV2 yet
+export async function query(
+  api: ApiPromise,
+  account: KeyringPair,
+  contract: ContractPromise,
+  message: string,
+  args?: unknown[],
+  value?: number,
+  gasLimit?: WeightV2 | { refTime?: any; proofSize?: any; }
+): Promise<{ output: Codec | null, result: ContractExecResultResult }> {
+  const msg = contract.abi.findMessage(message);
+  const callResult = await api.call.contractsApi.call(account.address, contract.address, value ? value : 0, gasLimit ? gasLimit : null, null, msg.toU8a(args ? args : []));
+  // Same logic as contracts UI, so should be fine.
+  // Refernce implementation: https://github.com/paritytech/contracts-ui/blob/e343221a0d5c1ae67122fe99028246e5bdf38c46/src/ui/hooks/useDecodedOutput.ts
+  const output = callResult.result.isOk && msg.returnType
+    ? contract.abi.registry.createTypeUnsafe(
+      msg.returnType.lookupName || msg.returnType.type,
+      [callResult.result.asOk.data.toU8a(true)],
+      { isPedantic: true }
+    )
+    : null;
+  expect(output !== null && typeof output === 'object' && 'Err' in output).toBeFalsy();
+  return { output, result: callResult.result };
+}

+ 2 - 1
integration/substrate/issue666.spec.ts

@@ -1,5 +1,5 @@
 import expect from 'expect';
-import { gasLimit, createConnection, deploy, transaction, aliceKeypair, } from './index';
+import { weight, createConnection, deploy, transaction, aliceKeypair, } from './index';
 import { ContractPromise } from '@polkadot/api-contract';
 import { ApiPromise } from '@polkadot/api';
 
@@ -28,6 +28,7 @@ describe('issue666 flip and inc', () => {
 
         let contract = new ContractPromise(conn, inc_contract.abi, inc_contract.address);
 
+        let gasLimit = await weight(conn, contract, "superFlip");
         contract.tx.superFlip({ gasLimit });
     });
 });

+ 4 - 3
integration/substrate/msg_sender.spec.ts

@@ -1,5 +1,5 @@
 import expect from "expect";
-import { aliceKeypair, createConnection, deploy, gasLimit, transaction } from "./index";
+import { aliceKeypair, createConnection, deploy, weight, transaction, query } from "./index";
 import { ContractPromise } from "@polkadot/api-contract";
 import { DecodedEvent } from "@polkadot/api-contract/types";
 import { ApiPromise } from "@polkadot/api";
@@ -23,10 +23,10 @@ describe('Deploy mytoken contract and test', () => {
         let deployed_contract = await deploy(conn, alice, 'mytoken.contract', BigInt(0));
         let contract = new ContractPromise(conn, deployed_contract.abi, deployed_contract.address);
 
-        let res = await contract.query.test(alice.address, {}, alice.address, true);
+        let res = await query(conn, alice, contract, "test", [alice.address, true]);
         expect(res.output?.toJSON()).toEqual(alice.address);
 
-        res = await contract.query.test(alice.address, {}, alice.address, false);
+        res = await query(conn, alice, contract, "test", [alice.address, false]);
         expect(res.output?.toJSON()).toEqual(alice.address);
     });
 
@@ -37,6 +37,7 @@ describe('Deploy mytoken contract and test', () => {
 
         let deployed_contract = await deploy(conn, alice, 'mytokenEvent.contract', BigInt(0));
         let contract = new ContractPromise(conn, deployed_contract.abi, deployed_contract.address);
+        let gasLimit = await weight(conn, contract, "test");
         let tx = contract.tx.test({ gasLimit });
         let res0: any = await transaction(tx, alice);
 

+ 4 - 4
integration/substrate/overloading.spec.ts

@@ -1,5 +1,5 @@
 import expect from 'expect';
-import { gasLimit, createConnection, deploy, transaction, aliceKeypair, } from './index';
+import { createConnection, deploy, transaction, aliceKeypair, query, } from './index';
 import { ContractPromise } from '@polkadot/api-contract';
 import { ApiPromise } from '@polkadot/api';
 
@@ -21,14 +21,14 @@ describe('Deploy contract with overloaded functions using mangled names', () =>
         let deploy_contract = await deploy(conn, alice, 'Overloading.contract', BigInt(0));
         let contract = new ContractPromise(conn, deploy_contract.abi, deploy_contract.address);
 
-        let res0 = await contract.query.echo(alice.address, {});
+        let res0 = await query(conn, alice, contract, "echo");
         expect(res0.output?.toJSON()).toEqual(42);
 
-        let res1 = await contract.query.echoUint32(alice.address, {}, 1234);
+        let res1 = await query(conn, alice, contract, "echoUint32", [1234]);
         expect(res1.output?.toJSON()).toEqual(1234);
 
         let someStruct = { s: "foo", e: [["v1", "v2"], ["v3", "v4"]] };
-        let res2 = await contract.query.echoBoolStringUint8Array2Array(alice.address, {}, true, someStruct);
+        let res2 = await query(conn, alice, contract, "echoBoolStringUint8Array2Array", [true, someStruct]);
         expect(res2.output?.toJSON()).toEqual(someStruct);
     });
 });

+ 11 - 2
integration/substrate/package.json

@@ -5,9 +5,18 @@
   "main": "index.js",
   "scripts": {
     "test": "tsc; ts-mocha -t 20000 *.spec.ts",
-    "build": "solang compile *.sol test/*.sol --target substrate -v"
+    "build": "parallel solang compile -v --target substrate ::: *.sol test/*.sol"
   },
-  "author": "Sean Young <sean@mess.org>",
+  "contributors": [
+    {
+      "name": "Sean Young",
+      "email": "sean@mess.org"
+    },
+    {
+      "name": "Cyrill Leutwiler",
+      "email": "me@09f9.org"
+    }
+  ],
   "license": "Apache-2.0",
   "devDependencies": {
     "@types/mocha": "^9.1.0",

+ 47 - 47
integration/substrate/primitives.spec.ts

@@ -1,5 +1,5 @@
 import expect from 'expect';
-import { createConnection, deploy, aliceKeypair, daveKeypair, } from './index';
+import { createConnection, deploy, aliceKeypair, daveKeypair, query, } from './index';
 import { ContractPromise } from '@polkadot/api-contract';
 import { ApiPromise } from '@polkadot/api';
 
@@ -27,117 +27,117 @@ describe('Deploy primitives contract and test', () => {
         // TEST Basic enums
         // in ethereum, an enum is described as an uint8 so can't use the enum
         // names programmatically. 0 = add, 1 = sub, 2 = mul, 3 = div, 4 = mod, 5 = pow, 6 = shl, 7 = shr
-        let res = await contract.query.isMul(alice.address, {}, 2);
+        let res = await query(conn, alice, contract, "isMul", [2]);
         expect(res.output?.toJSON()).toEqual(true);
 
-        res = await contract.query.returnDiv(alice.address, {});
+        res = await query(conn, alice, contract, "returnDiv");
         expect(res.output?.toJSON()).toEqual("div");
 
         // TEST uint and int types, and arithmetic/bitwise ops
-        res = await contract.query.opI64(alice.address, {}, 0, 1000, 4100);
+        res = await query(conn, alice, contract, "opI64", [0, 1000, 4100]);
         expect(res.output?.toJSON()).toEqual(5100);
-        res = await contract.query.opI64(alice.address, {}, 1, 1000, 4100);
+        res = await query(conn, alice, contract, "opI64", [1, 1000, 4100]);
         expect(res.output?.toJSON()).toEqual(-3100);
-        res = await contract.query.opI64(alice.address, {}, 2, 1000, 4100);
+        res = await query(conn, alice, contract, "opI64", [2, 1000, 4100]);
         expect(res.output?.toJSON()).toEqual(4100000);
-        res = await contract.query.opI64(alice.address, {}, 3, 1000, 10);
+        res = await query(conn, alice, contract, "opI64", [3, 1000, 10]);
         expect(res.output?.toJSON()).toEqual(100);
-        res = await contract.query.opI64(alice.address, {}, 4, 1000, 99);
+        res = await query(conn, alice, contract, "opI64", [4, 1000, 99]);
         expect(res.output?.toJSON()).toEqual(10);
-        res = await contract.query.opI64(alice.address, {}, 6, - 1000, 8);
+        res = await query(conn, alice, contract, "opI64", [6, - 1000, 8]);
         expect(res.output?.toJSON()).toEqual(-256000);
-        res = await contract.query.opI64(alice.address, {}, 7, - 1000, 8);
+        res = await query(conn, alice, contract, "opI64", [7, - 1000, 8]);
         expect(res.output?.toJSON()).toEqual(-4);
 
 
-        res = await contract.query.opU64(alice.address, {}, 0, 1000, 4100);
+        res = await query(conn, alice, contract, "opU64", [0, 1000, 4100]);
         expect(res.output?.toJSON()).toEqual(5100);
-        res = await contract.query.opU64(alice.address, {}, 1, 1000, 4100);
+        res = await query(conn, alice, contract, "opU64", [1, 1000, 4100]);
         expect(res.output?.toString()).toEqual("18446744073709548516"); // (2^64)-18446744073709548516 = 3100
-        res = await contract.query.opU64(alice.address, {}, 2, 123456789, 123456789);
+        res = await query(conn, alice, contract, "opU64", [2, 123456789, 123456789]);
         expect(res.output?.toString()).toEqual("15241578750190521");
-        res = await contract.query.opU64(alice.address, {}, 3, 123456789, 100);
+        res = await query(conn, alice, contract, "opU64", [3, 123456789, 100]);
         expect(res.output?.toJSON()).toEqual(1234567);
-        res = await contract.query.opU64(alice.address, {}, 4, 123456789, 100);
+        res = await query(conn, alice, contract, "opU64", [4, 123456789, 100]);
         expect(res.output?.toJSON()).toEqual(89);
-        res = await contract.query.opU64(alice.address, {}, 5, 3, 7);
+        res = await query(conn, alice, contract, "opU64", [5, 3, 7]);
         expect(res.output?.toJSON()).toEqual(2187);
-        res = await contract.query.opI64(alice.address, {}, 6, 1000, 8);
+        res = await query(conn, alice, contract, "opI64", [6, 1000, 8]);
         expect(res.output?.toJSON()).toEqual(256000);
-        res = await contract.query.opI64(alice.address, {}, 7, 1000, 8);
+        res = await query(conn, alice, contract, "opI64", [7, 1000, 8]);
         expect(res.output?.toJSON()).toEqual(3);
 
         // // now for 256 bit operations
-        res = await contract.query.opI256(alice.address, {}, 0, 1000, 4100);
+        res = await query(conn, alice, contract, "opI256", [0, 1000, 4100]);
         expect(res.output?.toJSON()).toEqual(5100);
-        res = await contract.query.opI256(alice.address, {}, 1, 1000, 4100);
+        res = await query(conn, alice, contract, "opI256", [1, 1000, 4100]);
         expect(res.output?.toJSON()).toEqual(-3100);
-        res = await contract.query.opI256(alice.address, {}, 2, 1000, 4100);
+        res = await query(conn, alice, contract, "opI256", [2, 1000, 4100]);
         expect(res.output?.toJSON()).toEqual(4100000);
-        res = await contract.query.opI256(alice.address, {}, 3, 1000, 10);
+        res = await query(conn, alice, contract, "opI256", [3, 1000, 10]);
         expect(res.output?.toJSON()).toEqual(100);
-        res = await contract.query.opI256(alice.address, {}, 4, 1000, 99);
+        res = await query(conn, alice, contract, "opI256", [4, 1000, 99]);
         expect(res.output?.toJSON()).toEqual(10);
-        res = await contract.query.opI256(alice.address, {}, 6, - 10000000000000, 8);
+        res = await query(conn, alice, contract, "opI256", [6, - 10000000000000, 8]);
         expect(res.output?.toJSON()).toEqual(-2560000000000000);
-        res = await contract.query.opI256(alice.address, {}, 7, - 10000000000000, 8);
+        res = await query(conn, alice, contract, "opI256", [7, - 10000000000000, 8]);
         expect(res.output?.toJSON()).toEqual(-39062500000);
 
-        res = await contract.query.opU256(alice.address, {}, 0, 1000, 4100);
+        res = await query(conn, alice, contract, "opU256", [0, 1000, 4100]);
         expect(res.output?.toJSON()).toEqual(5100);
-        res = await contract.query.opU256(alice.address, {}, 1, 1000, 4100);
+        res = await query(conn, alice, contract, "opU256", [1, 1000, 4100]);
         expect(res.output?.toString()).toEqual('115792089237316195423570985008687907853269984665640564039457584007913129636836'); // (2^64)-18446744073709548516 = 3100
-        res = await contract.query.opU256(alice.address, {}, 2, 123456789, 123456789);
+        res = await query(conn, alice, contract, "opU256", [2, 123456789, 123456789]);
         expect(res.output?.toString()).toEqual('15241578750190521');
-        res = await contract.query.opU256(alice.address, {}, 3, 123456789, 100);
+        res = await query(conn, alice, contract, "opU256", [3, 123456789, 100]);
         expect(res.output?.toJSON()).toEqual(1234567);
-        res = await contract.query.opU256(alice.address, {}, 4, 123456789, 100);
+        res = await query(conn, alice, contract, "opU256", [4, 123456789, 100]);
         expect(res.output?.toJSON()).toEqual(89);
-        res = await contract.query.opU256(alice.address, {}, 5, 123456789, 9);
+        res = await query(conn, alice, contract, "opU256", [5, 123456789, 9]);
         expect(res.output?.toString()).toEqual('6662462759719942007440037531362779472290810125440036903063319585255179509');
-        res = await contract.query.opI256(alice.address, {}, 6, 10000000000000, 8);
+        res = await query(conn, alice, contract, "opI256", [6, 10000000000000, 8]);
         expect(res.output?.toJSON()).toEqual(2560000000000000);
-        res = await contract.query.opI256(alice.address, {}, 7, 10000000000000, 8);
+        res = await query(conn, alice, contract, "opI256", [7, 10000000000000, 8]);
         expect(res.output?.toJSON()).toEqual(39062500000);
 
         // TEST bytesN
-        res = await contract.query.returnU86(alice.address, {},);
+        res = await query(conn, alice, contract, "returnU86");
         expect(res.output?.toJSON()).toEqual('0x414243444546');
 
         // TEST bytes5
-        res = await contract.query.opU85Shift(alice.address, {}, 6, '0xdeadcafe59', 8);
+        res = await query(conn, alice, contract, "opU85Shift", [6, '0xdeadcafe59', 8]);
         expect(res.output?.toJSON()).toEqual('0xadcafe5900');
-        res = await contract.query.opU85Shift(alice.address, {}, 7, '0xdeadcafe59', 8);
+        res = await query(conn, alice, contract, "opU85Shift", [7, '0xdeadcafe59', 8]);
         expect(res.output?.toJSON()).toEqual('0x00deadcafe');
-        res = await contract.query.opU85(alice.address, {}, 8, '0xdeadcafe59', '0x0000000006');
+        res = await query(conn, alice, contract, "opU85", [8, '0xdeadcafe59', '0x0000000006']);
         expect(res.output?.toJSON()).toEqual('0xdeadcafe5f');
-        res = await contract.query.opU85(alice.address, {}, 9, '0xdeadcafe59', '0x00000000ff');
+        res = await query(conn, alice, contract, "opU85", [9, '0xdeadcafe59', '0x00000000ff']);
         expect(res.output?.toJSON()).toEqual('0x0000000059');
-        res = await contract.query.opU85(alice.address, {}, 10, '0xdeadcafe59', '0x00000000ff');
+        res = await query(conn, alice, contract, "opU85", [10, '0xdeadcafe59', '0x00000000ff']);
         expect(res.output?.toJSON()).toEqual('0xdeadcafea6');
 
         // TEST bytes14
-        res = await contract.query.opU814Shift(alice.address, {}, 6, '0xdeadcafe123456789abcdefbeef7', 9);
+        res = await query(conn, alice, contract, "opU814Shift", [6, '0xdeadcafe123456789abcdefbeef7', 9]);
         expect(res.output?.toJSON()).toEqual('0x5b95fc2468acf13579bdf7ddee00');
-        res = await contract.query.opU814Shift(alice.address, {}, 7, '0xdeadcafe123456789abcdefbeef7', 9);
+        res = await query(conn, alice, contract, "opU814Shift", [7, '0xdeadcafe123456789abcdefbeef7', 9]);
         expect(res.output?.toJSON()).toEqual('0x006f56e57f091a2b3c4d5e6f7df7');
-        res = await contract.query.opU814(alice.address, {}, 8, '0xdeadcafe123456789abcdefbeef7', '0x0000060000000000000000000000');
+        res = await query(conn, alice, contract, "opU814", [8, '0xdeadcafe123456789abcdefbeef7', '0x0000060000000000000000000000']);
         expect(res.output?.toJSON()).toEqual('0xdeadcefe123456789abcdefbeef7');
-        res = await contract.query.opU814(alice.address, {}, 9, '0xdeadcafe123456789abcdefbeef7', '0x000000000000000000ff00000000');
+        res = await query(conn, alice, contract, "opU814", [9, '0xdeadcafe123456789abcdefbeef7', '0x000000000000000000ff00000000']);
         expect(res.output?.toJSON()).toEqual('0x000000000000000000bc00000000');
-        res = await contract.query.opU814(alice.address, {}, 10, '0xdeadcafe123456789abcdefbeef7', '0xff00000000000000000000000000');
+        res = await query(conn, alice, contract, "opU814", [10, '0xdeadcafe123456789abcdefbeef7', '0xff00000000000000000000000000']);
         expect(res.output?.toJSON()).toEqual('0x21adcafe123456789abcdefbeef7');
 
         // TEST address type.
         const default_account = '5GBWmgdFAMqm8ZgAHGobqDqX6tjLxJhv53ygjNtaaAn3sjeZ';
 
-        res = await contract.query.addressPassthrough(alice.address, {}, default_account);
+        res = await query(conn, alice, contract, "addressPassthrough", [default_account]);
         expect(res.output?.toJSON()).toEqual(default_account);
 
-        res = await contract.query.addressPassthrough(alice.address, {}, dave.address);
+        res = await query(conn, alice, contract, "addressPassthrough", [dave.address]);
         expect(res.output?.toJSON()).toEqual(dave.address);
 
-        res = await contract.query.addressPassthrough(alice.address, {}, alice.address);
+        res = await query(conn, alice, contract, "addressPassthrough", [alice.address]);
         expect(res.output?.toJSON()).toEqual(alice.address);
     });
 });

+ 5 - 1
integration/substrate/store.spec.ts

@@ -1,5 +1,5 @@
 import expect from 'expect';
-import { gasLimit, createConnection, deploy, transaction, aliceKeypair, } from './index';
+import { weight, createConnection, deploy, transaction, aliceKeypair, } from './index';
 import { ContractPromise } from '@polkadot/api-contract';
 import { ApiPromise } from '@polkadot/api';
 
@@ -33,6 +33,7 @@ describe.skip('Deploy store contract and test', () => {
 
         expect(res2.output?.toJSON()).toStrictEqual([0, "", "0xb00b1e", "0x00000000", "bar1"]);
 
+        let gasLimit = await weight(conn, contract, "setValues");
         const tx1 = contract.tx.setValues({ gasLimit });
 
         await transaction(tx1, alice);
@@ -55,6 +56,7 @@ describe.skip('Deploy store contract and test', () => {
             "bar2",
         ]);
 
+        gasLimit = await weight(conn, contract, "doOps");
         const tx2 = contract.tx.doOps({ gasLimit });
 
         await transaction(tx2, alice);
@@ -78,6 +80,7 @@ describe.skip('Deploy store contract and test', () => {
             "bar4",
         ]);
 
+        gasLimit = await weight(conn, contract, "pushZero");
         const tx3 = contract.tx.pushZero({ gasLimit });
 
         await transaction(tx3, alice);
@@ -94,6 +97,7 @@ describe.skip('Deploy store contract and test', () => {
 
                 val = val.length == 1 ? "0" + val : val;
 
+                gasLimit = await weight(conn, contract, "push", ["0x" + val]);
                 const tx = contract.tx.push({ gasLimit }, ["0x" + val]);
 
                 await transaction(tx, alice);

+ 21 - 19
integration/substrate/structs.spec.ts

@@ -1,5 +1,5 @@
 import expect from 'expect';
-import { gasLimit, createConnection, deploy, transaction, aliceKeypair, } from './index';
+import { createConnection, deploy, transaction, aliceKeypair, weight, query, } from './index';
 import { ContractPromise } from '@polkadot/api-contract';
 import { ApiPromise } from '@polkadot/api';
 
@@ -23,11 +23,12 @@ describe('Deploy struct contract and test', () => {
 
         let contract = new ContractPromise(conn, deployed_contract.abi, deployed_contract.address);
 
+        let gasLimit = await weight(conn, contract, "setFoo1");
         const tx1 = contract.tx.setFoo1({ gasLimit });
 
         await transaction(tx1, alice);
 
-        let res1 = await contract.query.getBothFoos(alice.address, {});
+        let res1 = await query(conn, alice, contract, "getBothFoos");
 
         expect(res1.output?.toJSON()).toStrictEqual([
             {
@@ -48,24 +49,23 @@ describe('Deploy struct contract and test', () => {
             }
         ]);
 
-        const tx2 = contract.tx.setFoo2({ gasLimit },
-            {
-                "f1": "bar2",
-                "f2": "0xb52b073595ccb35eaebb87178227b779",
-                "f3": -123112321,
-                "f4": "0x123456",
-                "f5": "Barking up the wrong tree",
-                "f6": {
-                    "in1": true, "in2": "Drive someone up the wall"
-                }
-            },
-            "nah"
-        );
+        const arg1 = {
+            "f1": "bar2",
+            "f2": "0xb52b073595ccb35eaebb87178227b779",
+            "f3": -123112321,
+            "f4": "0x123456",
+            "f5": "Barking up the wrong tree",
+            "f6": {
+                "in1": true, "in2": "Drive someone up the wall"
+            }
+        };
+        gasLimit = await weight(conn, contract, "setFoo2", [arg1, "nah"]);
+        const tx2 = contract.tx.setFoo2({ gasLimit }, arg1, "nah");
 
         await transaction(tx2, alice);
 
         if (1) {
-            let res3 = await contract.query.getFoo(alice.address, {}, false);
+            let res3 = await query(conn, alice, contract, "getFoo", [false]);
 
             expect(res3.output?.toJSON()).toStrictEqual(
                 {
@@ -79,7 +79,7 @@ describe('Deploy struct contract and test', () => {
             );
         }
 
-        let res2 = await contract.query.getBothFoos(alice.address, {});
+        let res2 = await query(conn, alice, contract, "getBothFoos");
 
         expect(res2.output?.toJSON()).toStrictEqual([
             {
@@ -100,11 +100,12 @@ describe('Deploy struct contract and test', () => {
             }
         ]);
 
+        gasLimit = await weight(conn, contract, "deleteFoo", [true]);
         const tx3 = contract.tx.deleteFoo({ gasLimit }, true);
 
         await transaction(tx3, alice);
 
-        let res3 = await contract.query.getFoo(alice.address, {}, false);
+        let res3 = await query(conn, alice, contract, "getFoo", [false]);
 
         expect(res3.output?.toJSON()).toStrictEqual(
             {
@@ -117,11 +118,12 @@ describe('Deploy struct contract and test', () => {
             },
         );
 
+        gasLimit = await weight(conn, contract, "structLiteral");
         const tx4 = contract.tx.structLiteral({ gasLimit });
 
         await transaction(tx4, alice);
 
-        let res4 = await contract.query.getFoo(alice.address, {}, true);
+        let res4 = await query(conn, alice, contract, "getFoo", [true]);
 
         expect(res4.output?.toJSON()).toStrictEqual(
             {

+ 7 - 4
src/linker/wasm.rs

@@ -70,11 +70,14 @@ pub fn link(input: &[u8], name: &str) -> Vec<u8> {
         while ind < imports.len() {
             if imports[ind].field().starts_with("seal") {
                 let module_name = match imports[ind].field() {
-                    "seal_instantiate" | "seal_terminate" | "seal_random" | "seal_call" => "seal1",
-                    "seal_set_storage"
-                    | "seal_clear_storage"
+                    "seal_set_storage" => "seal2",
+                    "seal_clear_storage"
                     | "seal_contains_storage"
-                    | "seal_get_storage" => "__unstable__",
+                    | "seal_get_storage"
+                    | "seal_instantiate"
+                    | "seal_terminate"
+                    | "seal_random"
+                    | "seal_call" => "seal1",
                     _ => "seal0",
                 };
                 *imports[ind].module_mut() = module_name.to_owned();

+ 1 - 1
tests/substrate.rs

@@ -948,7 +948,7 @@ impl MockSubstrate {
                 .with_resolver("env", self)
                 .with_resolver("seal0", self)
                 .with_resolver("seal1", self)
-                .with_resolver("__unstable__", self),
+                .with_resolver("seal2", self),
         )
         .expect("Failed to instantiate module")
         .run_start(&mut NopExternals)