index.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import '@polkadot/api-augment';
  2. import fs, { PathLike } from 'fs';
  3. import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';
  4. import { convertWeight } from '@polkadot/api-contract/base/util';
  5. import { CodePromise, ContractPromise } from '@polkadot/api-contract';
  6. import { SubmittableExtrinsic } from '@polkadot/api/types';
  7. import { AnyNumber, Codec, ISubmittableResult } from '@polkadot/types/types';
  8. import { KeyringPair } from '@polkadot/keyring/types';
  9. import expect from 'expect';
  10. import { ContractExecResultResult, WeightV2 } from '@polkadot/types/interfaces';
  11. const default_url = "ws://127.0.0.1:9944";
  12. export function aliceKeypair(): KeyringPair {
  13. const keyring = new Keyring({ type: 'sr25519' });
  14. return keyring.addFromUri('//Alice');
  15. }
  16. export function daveKeypair(): KeyringPair {
  17. const keyring = new Keyring({ type: 'sr25519' });
  18. return keyring.addFromUri('//Dave');
  19. }
  20. export function createConnection(): Promise<ApiPromise> {
  21. const url = process.env.RPC_URL || default_url;
  22. return ApiPromise.create({ provider: new WsProvider(url) });
  23. }
  24. export function deploy(api: ApiPromise, pair: KeyringPair, file: PathLike, value: bigint, ...params: unknown[]): Promise<any> {
  25. const contractJson = fs.readFileSync(file, { encoding: 'utf-8' });
  26. const code = new CodePromise(api, contractJson, null);
  27. const gasLimit = api.registry.createType('WeightV2', { refTime: 100000n * 1000000n, proofSize: 100000n });
  28. const tx = code.tx.new({ gasLimit, value }, ...params);
  29. return new Promise(async (resolve, reject) => {
  30. const unsub = await tx.signAndSend(pair, (result: any) => {
  31. if (result.status.isInBlock || result.status.isFinalized) {
  32. resolve(result.contract);
  33. unsub();
  34. }
  35. if (result.isError) {
  36. if (result.dispatchError) {
  37. console.log(result.dispatchError.toHuman());
  38. } else {
  39. console.log(result.asError.toHuman());
  40. }
  41. reject(result);
  42. unsub();
  43. }
  44. });
  45. });
  46. }
  47. export function transaction(tx: SubmittableExtrinsic<"promise", ISubmittableResult>, pair: KeyringPair): Promise<ISubmittableResult> {
  48. return new Promise(async (resolve, reject) => {
  49. const unsub = await tx.signAndSend(pair, (result: any) => {
  50. if (result.dispatchError) {
  51. console.log(`dispatchError:${JSON.stringify(result)}`)
  52. reject(result);
  53. unsub();
  54. }
  55. if (result.isError) {
  56. console.log(`isError:${JSON.stringify(result)}`)
  57. reject(result);
  58. unsub();
  59. }
  60. if (result.status.isInBlock || result.status.isFinalized) {
  61. resolve(result);
  62. unsub();
  63. }
  64. });
  65. });
  66. }
  67. // Returns the required gas estimated from a dry run
  68. export async function weight(api: ApiPromise, contract: ContractPromise, message: string, args?: unknown[], value?: AnyNumber) {
  69. let res = await dry_run(api, contract, message, args, value);
  70. return res.gasRequired
  71. }
  72. // Returns the debug buffer from the dry run result
  73. export async function debug_buffer(api: ApiPromise, contract: ContractPromise, message: string, args?: unknown[], value?: AnyNumber) {
  74. let res = await dry_run(api, contract, message, args, value);
  75. return res.debugMessage.toHuman()
  76. }
  77. // Return dry run result
  78. export async function dry_run(api: ApiPromise, contract: ContractPromise, message: string, args?: unknown[], value?: AnyNumber) {
  79. const ALICE = new Keyring({ type: 'sr25519' }).addFromUri('//Alice').address;
  80. const msg = contract.abi.findMessage(message);
  81. const dry = await api.call.contractsApi.call(ALICE, contract.address, value ? value : 0, null, null, msg.toU8a(args ? args : []));
  82. return dry;
  83. }
  84. // FIXME: The old contract.query API does not support WeightV2 yet
  85. export async function query(
  86. api: ApiPromise,
  87. account: KeyringPair,
  88. contract: ContractPromise,
  89. message: string,
  90. args?: unknown[],
  91. value?: number,
  92. gasLimit?: WeightV2 | { refTime?: any; proofSize?: any; }
  93. ): Promise<{ output: Codec | null, result: ContractExecResultResult }> {
  94. const msg = contract.abi.findMessage(message);
  95. const callResult = await api.call.contractsApi.call(account.address, contract.address, value ? value : 0, gasLimit ? gasLimit : null, null, msg.toU8a(args ? args : []));
  96. // Same logic as contracts UI, so should be fine.
  97. // Refernce implementation: https://github.com/paritytech/contracts-ui/blob/e343221a0d5c1ae67122fe99028246e5bdf38c46/src/ui/hooks/useDecodedOutput.ts
  98. const output = callResult.result.isOk && msg.returnType && callResult.result.asOk.flags.isEmpty
  99. ? contract.abi.registry.createTypeUnsafe(
  100. msg.returnType.lookupName || msg.returnType.type,
  101. [callResult.result.asOk.data.toU8a(true)],
  102. { isPedantic: true }
  103. )
  104. : null;
  105. expect(output !== null && typeof output === 'object' && 'Err' in output).toBeFalsy();
  106. return { output, result: callResult.result };
  107. }