index.ts 4.8 KB

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