diff --git a/canister_templates/experimental.wasm b/canister_templates/experimental.wasm index e0b34d1dea..f47f454d2a 100644 Binary files a/canister_templates/experimental.wasm and b/canister_templates/experimental.wasm differ diff --git a/canister_templates/stable.wasm b/canister_templates/stable.wasm index 871ec4eba8..c05d03ad8d 100644 Binary files a/canister_templates/stable.wasm and b/canister_templates/stable.wasm differ diff --git a/examples/experimental/demo/ckbtc/wallet/backend/index.ts b/examples/experimental/demo/ckbtc/wallet/backend/index.ts index acc8ebd758..5cc20cb3ab 100644 --- a/examples/experimental/demo/ckbtc/wallet/backend/index.ts +++ b/examples/experimental/demo/ckbtc/wallet/backend/index.ts @@ -17,28 +17,53 @@ export default class { @update([], IDL.Nat64) async getBalance(): Promise { - return await call(this.ckBtcPrincipal, 'icrc1_balance_of', { - paramIdlTypes: [Account], - returnIdlType: IDL.Nat, + return await call<[Account], bigint>( + this.ckBtcPrincipal, + 'icrc1_balance_of', + { + paramIdlTypes: [Account], + returnIdlType: IDL.Nat, + args: [ + { + owner: canisterSelf(), + subaccount: [ + padPrincipalWithZeros(msgCaller().toUint8Array()) + ] + } + ] + } + ); + } + + @update([], UpdateBalanceResult) + async updateBalance(): Promise { + const updateBalanceResult: UpdateBalanceResult = await call< + [UpdateBalanceArgs], + UpdateBalanceResult + >(this.minterPrincipal, 'update_balance', { + paramIdlTypes: [UpdateBalanceArgs], + returnIdlType: UpdateBalanceResult, args: [ { - owner: canisterSelf(), + owner: [canisterSelf()], subaccount: [ padPrincipalWithZeros(msgCaller().toUint8Array()) ] } ] }); + + return updateBalanceResult; } - @update([], UpdateBalanceResult) - async updateBalance(): Promise { - const updateBalanceResult: UpdateBalanceResult = await call( + @update([], IDL.Text) + async getDepositAddress(): Promise { + return await call<[GetBtcAddressArgs], string>( this.minterPrincipal, - 'update_balance', + 'get_btc_address', { - paramIdlTypes: [UpdateBalanceArgs], - returnIdlType: UpdateBalanceResult, + paramIdlTypes: [GetBtcAddressArgs], + returnIdlType: IDL.Text, args: [ { owner: [canisterSelf()], @@ -49,52 +74,38 @@ export default class { ] } ); - - return updateBalanceResult; - } - - @update([], IDL.Text) - async getDepositAddress(): Promise { - return await call(this.minterPrincipal, 'get_btc_address', { - paramIdlTypes: [GetBtcAddressArgs], - returnIdlType: IDL.Text, - args: [ - { - owner: [canisterSelf()], - subaccount: [ - padPrincipalWithZeros(msgCaller().toUint8Array()) - ] - } - ] - }); } // TODO get rid of Result @update([IDL.Text, IDL.Nat], TransferResult) async transfer(to: string, amount: bigint): Promise { - return await call(this.ckBtcPrincipal, 'icrc1_transfer', { - paramIdlTypes: [TransferArgs], - returnIdlType: TransferResult, - args: [ - { - from_subaccount: [ - padPrincipalWithZeros(msgCaller().toUint8Array()) - ], - to: { - owner: canisterSelf(), - subaccount: [ - padPrincipalWithZeros( - Principal.fromText(to).toUint8Array() - ) - ] - }, - amount, - fee: [], - memo: [], - created_at_time: [] - } - ] - }); + return await call<[TransferArgs], TransferResult>( + this.ckBtcPrincipal, + 'icrc1_transfer', + { + paramIdlTypes: [TransferArgs], + returnIdlType: TransferResult, + args: [ + { + from_subaccount: [ + padPrincipalWithZeros(msgCaller().toUint8Array()) + ], + to: { + owner: canisterSelf(), + subaccount: [ + padPrincipalWithZeros( + Principal.fromText(to).toUint8Array() + ) + ] + }, + amount, + fee: [], + memo: [], + created_at_time: [] + } + ] + } + ); } } diff --git a/examples/experimental/demo/ckbtc/wallet/backend/minter.ts b/examples/experimental/demo/ckbtc/wallet/backend/minter.ts index e91764a9c0..6c461d0c88 100644 --- a/examples/experimental/demo/ckbtc/wallet/backend/minter.ts +++ b/examples/experimental/demo/ckbtc/wallet/backend/minter.ts @@ -1,4 +1,4 @@ -import { IDL } from 'azle'; +import { IDL, Principal } from 'azle'; import { UpdateBalanceError, @@ -59,11 +59,19 @@ export const GetBtcAddressArgs = IDL.Record({ owner: IDL.Opt(IDL.Principal), subaccount: IDL.Opt(IDL.Vec(IDL.Nat8)) }); +export type GetBtcAddressArgs = { + owner: [Principal] | []; + subaccount: [Uint8Array] | []; +}; export const UpdateBalanceArgs = IDL.Record({ owner: IDL.Opt(IDL.Principal), subaccount: IDL.Opt(IDL.Vec(IDL.Nat8)) }); +export type UpdateBalanceArgs = { + owner: [Principal] | []; + subaccount: [Uint8Array] | []; +}; export const Minter = IDL.Service({ get_btc_address: IDL.Func([GetBtcAddressArgs], [IDL.Text]), diff --git a/examples/experimental/test/end_to_end/candid_rpc/async_await/src/async_await.ts b/examples/experimental/test/end_to_end/candid_rpc/async_await/src/async_await.ts index b438f40f48..20cb6d7638 100644 --- a/examples/experimental/test/end_to_end/candid_rpc/async_await/src/async_await.ts +++ b/examples/experimental/test/end_to_end/candid_rpc/async_await/src/async_await.ts @@ -9,7 +9,7 @@ export default Canister({ return responseJson; } else { - return await call('aaaaa-aa', 'raw_rand', { + return await call('aaaaa-aa', 'raw_rand', { returnIdlType: IDL.Vec(IDL.Nat8) }); } @@ -32,7 +32,7 @@ export default Canister({ if (process.env.AZLE_TEST_FETCH === 'true') { await fetch(`icp://aaaaa-aa/raw_rand`); } else { - await call('aaaaa-aa', 'raw_rand', { + await call('aaaaa-aa', 'raw_rand', { returnIdlType: IDL.Vec(IDL.Nat8) }); } @@ -58,7 +58,7 @@ async function getRandomness(): Promise { return responseJson; } else { - return await call('aaaaa-aa', 'raw_rand', { + return await call('aaaaa-aa', 'raw_rand', { returnIdlType: IDL.Vec(IDL.Nat8) }); } diff --git a/examples/experimental/test/end_to_end/candid_rpc/call_raw/src/index.ts b/examples/experimental/test/end_to_end/candid_rpc/call_raw/src/index.ts index b606bae370..da8fadb1d3 100644 --- a/examples/experimental/test/end_to_end/candid_rpc/call_raw/src/index.ts +++ b/examples/experimental/test/end_to_end/candid_rpc/call_raw/src/index.ts @@ -6,10 +6,15 @@ export default Canister({ [Principal, text, text, nat], text, async (canisterId, method, candidArgs, payment) => { - const result = await call(canisterId, method, { - raw: candidEncode(candidArgs), - cycles: payment - }); + const result = await call( + canisterId, + method, + { + args: candidEncode(candidArgs), + cycles: payment, + raw: true + } + ); return candidDecode(result); } diff --git a/examples/experimental/test/end_to_end/candid_rpc/canister/src/index.ts b/examples/experimental/test/end_to_end/candid_rpc/canister/src/index.ts index a38a2c6733..1f53206830 100644 --- a/examples/experimental/test/end_to_end/candid_rpc/canister/src/index.ts +++ b/examples/experimental/test/end_to_end/candid_rpc/canister/src/index.ts @@ -51,9 +51,13 @@ export default Canister({ return responseJson; } else { - return await call(getSomeCanisterPrincipal(), 'update1', { - returnIdlType: IDL.Text - }); + return await call( + getSomeCanisterPrincipal(), + 'update1', + { + returnIdlType: IDL.Text + } + ); } }) }); diff --git a/examples/experimental/test/end_to_end/candid_rpc/ckbtc/wallet/backend/index.ts b/examples/experimental/test/end_to_end/candid_rpc/ckbtc/wallet/backend/index.ts index fc3152676a..85d29d9e43 100644 --- a/examples/experimental/test/end_to_end/candid_rpc/ckbtc/wallet/backend/index.ts +++ b/examples/experimental/test/end_to_end/candid_rpc/ckbtc/wallet/backend/index.ts @@ -100,7 +100,7 @@ export default Canister({ return responseJson; } else { - return await call<[Account], nat>( + return await call<[Account], bigint>( getCkBtcPrincipal(), 'icrc1_balance_of', { @@ -162,7 +162,7 @@ export default Canister({ return responseJson; } else { - return await call<[AccountArg], text>( + return await call<[AccountArg], string>( getMinterPrincipal(), 'get_btc_address', { diff --git a/examples/experimental/test/end_to_end/candid_rpc/composite_queries/src/canister1/index.ts b/examples/experimental/test/end_to_end/candid_rpc/composite_queries/src/canister1/index.ts index 93bea5713a..463df71056 100644 --- a/examples/experimental/test/end_to_end/candid_rpc/composite_queries/src/canister1/index.ts +++ b/examples/experimental/test/end_to_end/candid_rpc/composite_queries/src/canister1/index.ts @@ -27,7 +27,7 @@ const CompQueryCanister = Canister({ return responseJson; } else { - return await call( + return await call( getCanister2Principal(), 'simpleQuery', { @@ -51,7 +51,7 @@ const CompQueryCanister = Canister({ return responseJson; } else { - return await call( + return await call( getCanister2Principal(), 'manualQuery', { @@ -82,7 +82,7 @@ const CompQueryCanister = Canister({ msgReply(encoded); } else { - const result = await call( + const result = await call( getCanister2Principal(), 'manualQuery', { @@ -114,7 +114,7 @@ const CompQueryCanister = Canister({ return responseJson; } else { - return await call( + return await call( getCanister2Principal(), 'deepQuery', { @@ -138,7 +138,7 @@ const CompQueryCanister = Canister({ return responseJson; } else { - return await call( + return await call( getCanister2Principal(), 'updateQuery', { @@ -162,7 +162,7 @@ const CompQueryCanister = Canister({ return responseJson; } else { - return await call( + return await call( getCanister2Principal(), 'simpleQuery', { @@ -186,7 +186,7 @@ const CompQueryCanister = Canister({ return responseJson; } else { - return await call( + return await call( getCanister2Principal(), 'deepQuery', { @@ -249,7 +249,7 @@ async function incCanister(canister: any, candidPath: string): Promise { return responseJson; } else { - return await call( + return await call( canister.principal.toText(), 'incCounter', { @@ -273,7 +273,7 @@ async function incCanister2(): Promise { return responseJson; } else { - return await call( + return await call( getCanister2Principal(), 'incCounter', { diff --git a/examples/experimental/test/end_to_end/candid_rpc/composite_queries/src/canister2/index.ts b/examples/experimental/test/end_to_end/candid_rpc/composite_queries/src/canister2/index.ts index 532f8250ed..512434641b 100644 --- a/examples/experimental/test/end_to_end/candid_rpc/composite_queries/src/canister2/index.ts +++ b/examples/experimental/test/end_to_end/candid_rpc/composite_queries/src/canister2/index.ts @@ -53,7 +53,7 @@ export default Canister({ return await response.json(); } else { - return await call( + return await call( canister3Principal, 'deepQuery', { diff --git a/examples/experimental/test/end_to_end/candid_rpc/composite_queries/test/tests.ts b/examples/experimental/test/end_to_end/candid_rpc/composite_queries/test/tests.ts index faa1b107cc..f9983efe96 100644 --- a/examples/experimental/test/end_to_end/candid_rpc/composite_queries/test/tests.ts +++ b/examples/experimental/test/end_to_end/candid_rpc/composite_queries/test/tests.ts @@ -34,7 +34,7 @@ export function getTests(canister1: ActorSubclass<_SERVICE>): Test { it('fails to perform a composite query attempting to call an update method', async () => { const canisterId = getCanisterId('canister2'); const partialErrorMessage = new RegExp( - `Rejection code 5, Error from Canister ${canisterId}: Canister has no query method` + `reject code 5: Error from Canister ${canisterId}: Canister has no query method` ); await expect(canister1.updateQuery()).rejects.toThrow( @@ -44,7 +44,7 @@ export function getTests(canister1: ActorSubclass<_SERVICE>): Test { it('fails to perform a composite query from a regular query', async () => { const partialErrorMessage = new RegExp( - `Rejection code 5, IC0527: Composite query cannot be called in replicated mode` + `reject code 5: IC0527: Composite query cannot be called in replicated mode` ); await expect(canister1.simpleUpdate()).rejects.toThrow( diff --git a/examples/experimental/test/end_to_end/candid_rpc/cross_canister_calls/src/canister1/index.ts b/examples/experimental/test/end_to_end/candid_rpc/cross_canister_calls/src/canister1/index.ts index 6a581a3965..0eb67c6c89 100644 --- a/examples/experimental/test/end_to_end/candid_rpc/cross_canister_calls/src/canister1/index.ts +++ b/examples/experimental/test/end_to_end/candid_rpc/cross_canister_calls/src/canister1/index.ts @@ -1,4 +1,4 @@ -import { call, notify } from 'azle'; +import { call } from 'azle'; import { Canister, nat64, @@ -30,7 +30,7 @@ export default Canister({ return responseJson; } else { - return await call<[text, text, nat64], nat64>( + return await call<[text, text, bigint], bigint>( getCanister2Principal(), 'transfer', { @@ -60,7 +60,7 @@ export default Canister({ return responseJson; } else { - return await call<[text], nat64>( + return await call<[text], bigint>( getCanister2Principal(), 'balance', { @@ -155,15 +155,17 @@ export default Canister({ ); } }), - sendNotification: update([], Void, () => { - // TODO for now there seems to be no fetch analogy because notify must be synchronous - // TODO and fetch must be asynchronous - // TODO though for azle stable we are going to make notify simply return a resolved promise immediately - return notify(getCanister2Principal(), 'receiveNotification', { - paramIdlTypes: [text.getIdlType()], - args: ['This is the notification'], - cycles: 10n - }); + sendNotification: update([], Void, async () => { + return await call<[string], void>( + getCanister2Principal(), + 'receiveNotification', + { + paramIdlTypes: [text.getIdlType()], + args: ['This is the notification'], + cycles: 10n, + oneway: true + } + ); }) }); diff --git a/examples/experimental/test/end_to_end/candid_rpc/cross_canister_calls/test/tests.ts b/examples/experimental/test/end_to_end/candid_rpc/cross_canister_calls/test/tests.ts index 61aeaaf8ba..82f74cf83d 100644 --- a/examples/experimental/test/end_to_end/candid_rpc/cross_canister_calls/test/tests.ts +++ b/examples/experimental/test/end_to_end/candid_rpc/cross_canister_calls/test/tests.ts @@ -103,7 +103,7 @@ export function getTests( const canister1Id = getCanisterId('canister1'); const canister2Id = getCanisterId('canister2'); const partialErrorMessage = new RegExp( - `Error from Canister ${canister1Id}: Canister called \`ic0.trap\` with message: Uncaught Error: Rejection code 5, IC0503: Error from Canister ${canister2Id}: Canister called \`ic0.trap\` with message: hahahaha` + `Error from Canister ${canister1Id}: Canister called \`ic0.trap\` with message: Uncaught Error: The inter-canister call failed with reject code 5: IC0503: Error from Canister ${canister2Id}: Canister called \`ic0.trap\` with message: hahahaha` ); await expect(canister1.trap()).rejects.toThrow(partialErrorMessage); diff --git a/examples/experimental/test/end_to_end/candid_rpc/cycles/src/intermediary/index.ts b/examples/experimental/test/end_to_end/candid_rpc/cycles/src/intermediary/index.ts index 59427310ac..c4f5bed819 100644 --- a/examples/experimental/test/end_to_end/candid_rpc/cycles/src/intermediary/index.ts +++ b/examples/experimental/test/end_to_end/candid_rpc/cycles/src/intermediary/index.ts @@ -1,4 +1,4 @@ -import { call, canisterCycleBalance, IDL, notify, trap } from 'azle'; +import { call, canisterCycleBalance, IDL, trap } from 'azle'; import { Canister, nat, @@ -35,10 +35,15 @@ export default Canister({ ); } }), - sendCyclesNotify: update([], Void, () => { - return notify(getCyclesPrincipal(), 'receiveCycles', { - cycles: 1_000_000n - }); + sendCyclesNotify: update([], Void, async () => { + return await call( + getCyclesPrincipal(), + 'receiveCycles', + { + cycles: 1_000_000n, + oneway: true + } + ); }), getCanisterCycleBalance: query([], nat, () => { return canisterCycleBalance(); diff --git a/examples/experimental/test/end_to_end/candid_rpc/icrc/canisters/proxy/index.ts b/examples/experimental/test/end_to_end/candid_rpc/icrc/canisters/proxy/index.ts index 3b43f8e162..6c5b4f22a1 100644 --- a/examples/experimental/test/end_to_end/candid_rpc/icrc/canisters/proxy/index.ts +++ b/examples/experimental/test/end_to_end/candid_rpc/icrc/canisters/proxy/index.ts @@ -102,7 +102,7 @@ export default Canister({ return responseJson; } else { - return await call( + return await call( icrcPrincipal, 'icrc1_decimals', { @@ -149,7 +149,7 @@ export default Canister({ return responseJson; } else { - return await call(icrcPrincipal, 'icrc1_fee', { + return await call(icrcPrincipal, 'icrc1_fee', { returnIdlType: IDL.Nat }); } @@ -170,7 +170,7 @@ export default Canister({ return responseJson; } else { - return await call( + return await call( icrcPrincipal, 'icrc1_total_supply', { @@ -236,7 +236,7 @@ export default Canister({ return responseJson; } else { - return await call<[Account], nat>( + return await call<[Account], bigint>( icrcPrincipal, 'icrc1_balance_of', { diff --git a/examples/experimental/test/end_to_end/candid_rpc/management_canister/src/index.ts b/examples/experimental/test/end_to_end/candid_rpc/management_canister/src/index.ts index 5537bb623a..b8bb04079f 100644 --- a/examples/experimental/test/end_to_end/candid_rpc/management_canister/src/index.ts +++ b/examples/experimental/test/end_to_end/candid_rpc/management_canister/src/index.ts @@ -33,8 +33,7 @@ import { query, serialize, update, - Vec, - Void + Vec } from 'azle/experimental'; import { CanisterInfoArgs, @@ -85,7 +84,7 @@ export default Canister({ }) }); } else { - await call<[update_settings_args], Void>( + await call<[update_settings_args], void>( 'aaaaa-aa', 'update_settings', { @@ -139,7 +138,7 @@ export default Canister({ }) }); } else { - await call<[clear_chunk_store_args], Void>( + await call<[clear_chunk_store_args], void>( 'aaaaa-aa', 'clear_chunk_store', { @@ -201,7 +200,7 @@ export default Canister({ }) }); } else { - await call<[install_code_args], Void>( + await call<[install_code_args], void>( 'aaaaa-aa', 'install_code', { @@ -239,7 +238,7 @@ export default Canister({ }) }); } else { - await call<[install_chunked_code_args], Void>( + await call<[install_chunked_code_args], void>( 'aaaaa-aa', 'install_chunked_code', { @@ -266,7 +265,7 @@ export default Canister({ }) }); } else { - await call<[uninstall_code_args], Void>( + await call<[uninstall_code_args], void>( 'aaaaa-aa', 'uninstall_code', { @@ -290,7 +289,7 @@ export default Canister({ }) }); } else { - await call<[start_canister_args], Void>( + await call<[start_canister_args], void>( 'aaaaa-aa', 'start_canister', { @@ -313,7 +312,7 @@ export default Canister({ }) }); } else { - await call<[stop_canister_args], Void>( + await call<[stop_canister_args], void>( 'aaaaa-aa', 'stop_canister', { @@ -392,7 +391,7 @@ export default Canister({ }) }); } else { - await call<[delete_canister_args], Void>( + await call<[delete_canister_args], void>( 'aaaaa-aa', 'delete_canister', { @@ -417,7 +416,7 @@ export default Canister({ }) }); } else { - await call<[deposit_cycles_args], Void>( + await call<[deposit_cycles_args], void>( 'aaaaa-aa', 'deposit_cycles', { @@ -497,7 +496,7 @@ export default Canister({ }) }); } else { - await call<[provisional_top_up_canister_args], Void>( + await call<[provisional_top_up_canister_args], void>( 'aaaaa-aa', 'provisional_top_up_canister', { diff --git a/examples/experimental/test/end_to_end/candid_rpc/notify_raw/src/canister1/index.ts b/examples/experimental/test/end_to_end/candid_rpc/notify_raw/src/canister1/index.ts index fe8dca0d29..3edae1215a 100644 --- a/examples/experimental/test/end_to_end/candid_rpc/notify_raw/src/canister1/index.ts +++ b/examples/experimental/test/end_to_end/candid_rpc/notify_raw/src/canister1/index.ts @@ -1,17 +1,18 @@ -import { candidEncode, notify, trap } from 'azle'; +import { call, trap } from 'azle'; import { Canister, Principal, update, Void } from 'azle/experimental'; export default Canister({ - sendNotification: update([], Void, () => { - return notify( + sendNotification: update([], Void, async () => { + return await call( Principal.fromText( process.env.CANISTER2_PRINCIPAL ?? trap('process.env.CANISTER2_PRINCIPAL is undefined') ), 'receiveNotification', { - raw: candidEncode('()'), - cycles: 0n + cycles: 0n, + oneway: true, + raw: true } ); }) diff --git a/examples/experimental/test/end_to_end/candid_rpc/outgoing_http_requests/src/index.ts b/examples/experimental/test/end_to_end/candid_rpc/outgoing_http_requests/src/index.ts index de8b55882d..def47decee 100644 --- a/examples/experimental/test/end_to_end/candid_rpc/outgoing_http_requests/src/index.ts +++ b/examples/experimental/test/end_to_end/candid_rpc/outgoing_http_requests/src/index.ts @@ -67,11 +67,11 @@ export default Canister({ [], Manual(HttpResponse), async () => { - const httpResponse = await call( + const httpResponse = await call( Principal.fromText('aaaaa-aa'), 'http_request', { - raw: candidEncode(` + args: candidEncode(` ( record { url = "https://xkcd.com/642/info.0.json"; @@ -83,7 +83,8 @@ export default Canister({ } ) `), - cycles: 50_000_000n + cycles: 50_000_000n, + raw: true } ); diff --git a/examples/experimental/test/end_to_end/candid_rpc/rejections/src/rejections/index.ts b/examples/experimental/test/end_to_end/candid_rpc/rejections/src/rejections/index.ts index d95bc3e280..3a46334245 100644 --- a/examples/experimental/test/end_to_end/candid_rpc/rejections/src/rejections/index.ts +++ b/examples/experimental/test/end_to_end/candid_rpc/rejections/src/rejections/index.ts @@ -20,7 +20,7 @@ export default Canister({ }) }); } else { - await call(someCanisterPrincipal, 'accept', { + await call(someCanisterPrincipal, 'accept', { returnIdlType: bool.getIdlType() }); } @@ -59,7 +59,7 @@ export default Canister({ }) }); } else { - await call<[text], empty>(someCanisterPrincipal, 'reject', { + await call<[text], never>(someCanisterPrincipal, 'reject', { paramIdlTypes: [text.getIdlType()], returnIdlType: empty.getIdlType(), args: ['reject'] @@ -82,7 +82,7 @@ export default Canister({ }) }); } else { - await call(someCanisterPrincipal, 'error', { + await call(someCanisterPrincipal, 'error', { returnIdlType: empty.getIdlType() }); } @@ -104,7 +104,7 @@ export default Canister({ }) }); } else { - await call<[text], empty>(someCanisterPrincipal, 'reject', { + await call<[text], never>(someCanisterPrincipal, 'reject', { paramIdlTypes: [text.getIdlType()], returnIdlType: empty.getIdlType(), args: [message] diff --git a/examples/stable/test/end_to_end/candid_rpc/async_await/src/async_await.ts b/examples/stable/test/end_to_end/candid_rpc/async_await/src/async_await.ts index 09823d0de5..911bf69295 100644 --- a/examples/stable/test/end_to_end/candid_rpc/async_await/src/async_await.ts +++ b/examples/stable/test/end_to_end/candid_rpc/async_await/src/async_await.ts @@ -3,7 +3,7 @@ import { call, IDL, update } from 'azle'; export default class { @update([], IDL.Vec(IDL.Nat8)) async getRandomnessDirectly(): Promise { - return await call('aaaaa-aa', 'raw_rand', { + return await call('aaaaa-aa', 'raw_rand', { returnIdlType: IDL.Vec(IDL.Nat8) }); } @@ -28,7 +28,7 @@ export default class { @update async returnPromiseVoid(): Promise { - await call('aaaaa-aa', 'raw_rand', { + await call('aaaaa-aa', 'raw_rand', { returnIdlType: IDL.Vec(IDL.Nat8) }); } @@ -47,7 +47,7 @@ async function getRandomnessLevel2(): Promise { } async function getRandomness(): Promise { - return await call('aaaaa-aa', 'raw_rand', { + return await call('aaaaa-aa', 'raw_rand', { returnIdlType: IDL.Vec(IDL.Nat8) }); } diff --git a/examples/stable/test/end_to_end/candid_rpc/call_raw/src/index.ts b/examples/stable/test/end_to_end/candid_rpc/call_raw/src/index.ts index f8f7fd2075..b8f5c7f567 100644 --- a/examples/stable/test/end_to_end/candid_rpc/call_raw/src/index.ts +++ b/examples/stable/test/end_to_end/candid_rpc/call_raw/src/index.ts @@ -8,9 +8,10 @@ export default class { candidArgs: string, cycles: bigint ): Promise { - const result = await call(canisterId, method, { - raw: candidEncode(candidArgs), - cycles + const result = await call(canisterId, method, { + args: candidEncode(candidArgs), + cycles, + raw: true }); return candidDecode(result); diff --git a/examples/stable/test/end_to_end/candid_rpc/canister/src/index.ts b/examples/stable/test/end_to_end/candid_rpc/canister/src/index.ts index 5455d2035e..6a7b8bf45f 100644 --- a/examples/stable/test/end_to_end/candid_rpc/canister/src/index.ts +++ b/examples/stable/test/end_to_end/candid_rpc/canister/src/index.ts @@ -34,7 +34,9 @@ export default class { @update([Canister], IDL.Text) async canisterCrossCanisterCall(someCanister: Canister): Promise { - return await call(someCanister, 'update1', { returnIdlType: IDL.Text }); + return await call(someCanister, 'update1', { + returnIdlType: IDL.Text + }); } } diff --git a/examples/stable/test/end_to_end/candid_rpc/composite_queries/src/canister1/index.ts b/examples/stable/test/end_to_end/candid_rpc/composite_queries/src/canister1/index.ts index 339f1af943..9afb3991b6 100644 --- a/examples/stable/test/end_to_end/candid_rpc/composite_queries/src/canister1/index.ts +++ b/examples/stable/test/end_to_end/candid_rpc/composite_queries/src/canister1/index.ts @@ -9,7 +9,7 @@ export default class Canister { composite: true }) async simpleCompositeQuery(): Promise { - return await call(this.canister2Id, 'simpleQuery', { + return await call(this.canister2Id, 'simpleQuery', { returnIdlType: IDL.Text }); } @@ -19,7 +19,7 @@ export default class Canister { composite: true }) async manualQuery(): Promise { - return await call(this.canister2Id, 'manualQuery', { + return await call(this.canister2Id, 'manualQuery', { returnIdlType: IDL.Text }); } @@ -48,7 +48,7 @@ export default class Canister { composite: true }) async deepQuery(): Promise { - return await call(this.canister2Id, 'deepQuery', { + return await call(this.canister2Id, 'deepQuery', { returnIdlType: IDL.Text }); } @@ -58,7 +58,7 @@ export default class Canister { composite: true }) async updateQuery(): Promise { - return await call(this.canister2Id, 'updateQuery', { + return await call(this.canister2Id, 'updateQuery', { returnIdlType: IDL.Text }); } @@ -66,7 +66,7 @@ export default class Canister { // Composite query being called by a query method. SHOULDN'T WORK @query([], IDL.Text) async simpleQuery(): Promise { - return await call(this.canister2Id, 'simpleQuery', { + return await call(this.canister2Id, 'simpleQuery', { returnIdlType: IDL.Text }); } @@ -74,7 +74,7 @@ export default class Canister { // Composite query being called by an update method. SHOULDN'T WORK @update([], IDL.Text) async simpleUpdate(): Promise { - return await call(this.canister2Id, 'deepQuery', { + return await call(this.canister2Id, 'deepQuery', { returnIdlType: IDL.Text }); } @@ -125,13 +125,13 @@ function getCanister2Id(): string { } async function incCanister(): Promise { - return await call(canisterSelf(), 'incCounter', { + return await call(canisterSelf(), 'incCounter', { returnIdlType: IDL.Nat }); } async function incCanister2(canister: Canister): Promise { - return await call(canister.canister2Id, 'incCounter', { + return await call(canister.canister2Id, 'incCounter', { returnIdlType: IDL.Nat }); } diff --git a/examples/stable/test/end_to_end/candid_rpc/composite_queries/src/canister2/index.ts b/examples/stable/test/end_to_end/candid_rpc/composite_queries/src/canister2/index.ts index 620ba1fe8c..453b75dff6 100644 --- a/examples/stable/test/end_to_end/candid_rpc/composite_queries/src/canister2/index.ts +++ b/examples/stable/test/end_to_end/candid_rpc/composite_queries/src/canister2/index.ts @@ -35,7 +35,7 @@ export default class { composite: true }) async deepQuery(): Promise { - return await call(this.canister3Id, 'deepQuery', { + return await call(this.canister3Id, 'deepQuery', { returnIdlType: IDL.Text }); } diff --git a/examples/stable/test/end_to_end/candid_rpc/cross_canister_calls/src/canister1/index.ts b/examples/stable/test/end_to_end/candid_rpc/cross_canister_calls/src/canister1/index.ts index b77880659b..4dc46ce644 100644 --- a/examples/stable/test/end_to_end/candid_rpc/cross_canister_calls/src/canister1/index.ts +++ b/examples/stable/test/end_to_end/candid_rpc/cross_canister_calls/src/canister1/index.ts @@ -1,4 +1,4 @@ -import { call, IDL, notify, update } from 'azle'; +import { call, IDL, update } from 'azle'; import { Account, AccountArgs } from '../canister2/types'; @@ -7,16 +7,20 @@ export default class { @update([IDL.Text, IDL.Text, IDL.Nat64], IDL.Nat64) async transfer(from: string, to: string, amount: bigint): Promise { - return await call(this.canister2Id, 'transfer', { - paramIdlTypes: [IDL.Text, IDL.Text, IDL.Nat64], - returnIdlType: IDL.Nat64, - args: [from, to, amount] - }); + return await call<[string, string, bigint], bigint>( + this.canister2Id, + 'transfer', + { + paramIdlTypes: [IDL.Text, IDL.Text, IDL.Nat64], + returnIdlType: IDL.Nat64, + args: [from, to, amount] + } + ); } @update([IDL.Text], IDL.Nat64) async balance(id: string): Promise { - return await call(this.canister2Id, 'balance', { + return await call<[string], bigint>(this.canister2Id, 'balance', { paramIdlTypes: [IDL.Text], returnIdlType: IDL.Nat64, args: [id] @@ -25,32 +29,41 @@ export default class { @update([AccountArgs], IDL.Opt(Account)) async account(args: AccountArgs): Promise<[Account] | []> { - return await call(this.canister2Id, 'account', { - paramIdlTypes: [AccountArgs], - returnIdlType: IDL.Opt(Account), - args: [args] - }); + return await call<[AccountArgs], [Account] | []>( + this.canister2Id, + 'account', + { + paramIdlTypes: [AccountArgs], + returnIdlType: IDL.Opt(Account), + args: [args] + } + ); } @update([], IDL.Vec(Account)) async accounts(): Promise { - return await call(this.canister2Id, 'accounts', { + return await call(this.canister2Id, 'accounts', { returnIdlType: IDL.Vec(Account) }); } @update([], IDL.Empty) async trap(): Promise { - return (await call(this.canister2Id, 'trap')) as never; + return await call(this.canister2Id, 'trap'); } @update - sendNotification(): void { - return notify(this.canister2Id, 'receiveNotification', { - paramIdlTypes: [IDL.Text], - args: ['This is the notification'], - cycles: 10n - }); + async sendNotification(): Promise { + return await call<[string], void>( + this.canister2Id, + 'receiveNotification', + { + paramIdlTypes: [IDL.Text], + args: ['This is the notification'], + cycles: 10n, + oneway: true + } + ); } } diff --git a/examples/stable/test/end_to_end/candid_rpc/cycles/src/intermediary/index.ts b/examples/stable/test/end_to_end/candid_rpc/cycles/src/intermediary/index.ts index 403c761589..8e63df5da2 100644 --- a/examples/stable/test/end_to_end/candid_rpc/cycles/src/intermediary/index.ts +++ b/examples/stable/test/end_to_end/candid_rpc/cycles/src/intermediary/index.ts @@ -1,4 +1,4 @@ -import { call, canisterCycleBalance, IDL, notify, query, update } from 'azle'; +import { call, canisterCycleBalance, IDL, query, update } from 'azle'; export default class { cyclesPrincipal = getCyclesPrincipal(); @@ -6,17 +6,26 @@ export default class { // Reports the number of cycles returned from the Cycles canister @update([], IDL.Nat64) async sendCycles(): Promise { - return await call(this.cyclesPrincipal, 'receiveCycles', { - returnIdlType: IDL.Nat64, - cycles: 1_000_000n - }); + return await call( + this.cyclesPrincipal, + 'receiveCycles', + { + returnIdlType: IDL.Nat64, + cycles: 1_000_000n + } + ); } @update - sendCyclesNotify(): void { - return notify(this.cyclesPrincipal, 'receiveCycles', { - cycles: 1_000_000n - }); + async sendCyclesNotify(): Promise { + return await call( + this.cyclesPrincipal, + 'receiveCycles', + { + cycles: 1_000_000n, + oneway: true + } + ); } @query([], IDL.Nat64) diff --git a/examples/stable/test/end_to_end/candid_rpc/func_types/canisters/func_types/index.ts b/examples/stable/test/end_to_end/candid_rpc/func_types/canisters/func_types/index.ts index 101c9f3fb6..054dcf4d42 100644 --- a/examples/stable/test/end_to_end/candid_rpc/func_types/canisters/func_types/index.ts +++ b/examples/stable/test/end_to_end/candid_rpc/func_types/canisters/func_types/index.ts @@ -116,9 +116,13 @@ export default class { @update([], NotifierFunc) async getNotifierFromNotifiersCanister(): Promise { - return await call(getNotifierPrincipal(), 'getNotifier', { - returnIdlType: NotifierFunc - }); + return await call( + getNotifierPrincipal(), + 'getNotifier', + { + returnIdlType: NotifierFunc + } + ); } } diff --git a/examples/stable/test/end_to_end/candid_rpc/heartbeat/src/heartbeat_async/index.ts b/examples/stable/test/end_to_end/candid_rpc/heartbeat/src/heartbeat_async/index.ts index b4a89571e0..cf09259543 100644 --- a/examples/stable/test/end_to_end/candid_rpc/heartbeat/src/heartbeat_async/index.ts +++ b/examples/stable/test/end_to_end/candid_rpc/heartbeat/src/heartbeat_async/index.ts @@ -18,7 +18,7 @@ export default class { } async function getRandomness(): Promise { - return await call('aaaaa-aa', 'raw_rand', { + return await call('aaaaa-aa', 'raw_rand', { returnIdlType: IDL.Vec(IDL.Nat8) }); } diff --git a/examples/stable/test/end_to_end/candid_rpc/icrc/canisters/proxy/index.ts b/examples/stable/test/end_to_end/candid_rpc/icrc/canisters/proxy/index.ts index 7bb707b9d1..d028dadf96 100644 --- a/examples/stable/test/end_to_end/candid_rpc/icrc/canisters/proxy/index.ts +++ b/examples/stable/test/end_to_end/candid_rpc/icrc/canisters/proxy/index.ts @@ -20,107 +20,151 @@ export default class { @query([], IDL.Vec(IDL.Tuple(IDL.Text, Value)), { composite: true }) async icrc1_metadata(): Promise<[Text, Value][]> { - return await call(this.icrcPrincipal, 'icrc1_metadata', { - returnIdlType: IDL.Vec(IDL.Tuple(IDL.Text, Value)) - }); + return await call( + this.icrcPrincipal, + 'icrc1_metadata', + { + returnIdlType: IDL.Vec(IDL.Tuple(IDL.Text, Value)) + } + ); } @query([], IDL.Text, { composite: true }) async icrc1_name(): Promise { - return await call(this.icrcPrincipal, 'icrc1_name', { + return await call(this.icrcPrincipal, 'icrc1_name', { returnIdlType: IDL.Text }); } @query([], IDL.Nat8, { composite: true }) async icrc1_decimals(): Promise { - return await call(this.icrcPrincipal, 'icrc1_decimals', { - returnIdlType: IDL.Nat8 - }); + return await call( + this.icrcPrincipal, + 'icrc1_decimals', + { + returnIdlType: IDL.Nat8 + } + ); } @query([], IDL.Text, { composite: true }) async icrc1_symbol(): Promise { - return await call(this.icrcPrincipal, 'icrc1_symbol', { - returnIdlType: IDL.Text - }); + return await call( + this.icrcPrincipal, + 'icrc1_symbol', + { + returnIdlType: IDL.Text + } + ); } @query([], IDL.Nat, { composite: true }) - async icrc1_fee(): Promise { - return await call(this.icrcPrincipal, 'icrc1_fee', { + async icrc1_fee(): Promise { + return await call(this.icrcPrincipal, 'icrc1_fee', { returnIdlType: IDL.Nat }); } @query([], IDL.Nat, { composite: true }) async icrc1_total_supply(): Promise { - return await call(this.icrcPrincipal, 'icrc1_total_supply', { - returnIdlType: IDL.Nat - }); + return await call( + this.icrcPrincipal, + 'icrc1_total_supply', + { + returnIdlType: IDL.Nat + } + ); } @query([], IDL.Opt(Account), { composite: true }) async icrc1_minting_account(): Promise<[Account] | []> { - return await call(this.icrcPrincipal, 'icrc1_minting_account', { - returnIdlType: IDL.Opt(Account) - }); + return await call( + this.icrcPrincipal, + 'icrc1_minting_account', + { + returnIdlType: IDL.Opt(Account) + } + ); } @query([Account], IDL.Nat, { composite: true }) async icrc1_balance_of(account: Account): Promise { - return await call(this.icrcPrincipal, 'icrc1_balance_of', { - paramIdlTypes: [Account], - returnIdlType: IDL.Nat, - args: [account] - }); + return await call<[Account], bigint>( + this.icrcPrincipal, + 'icrc1_balance_of', + { + paramIdlTypes: [Account], + returnIdlType: IDL.Nat, + args: [account] + } + ); } @update([TransferArgs], TransferResult) async icrc1_transfer(transferArgs: TransferArgs): Promise { - return await call(this.icrcPrincipal, 'icrc1_transfer', { - paramIdlTypes: [TransferArgs], - returnIdlType: TransferResult, - args: [transferArgs] - }); + return await call<[TransferArgs], TransferResult>( + this.icrcPrincipal, + 'icrc1_transfer', + { + paramIdlTypes: [TransferArgs], + returnIdlType: TransferResult, + args: [transferArgs] + } + ); } @query([], IDL.Vec(SupportedStandard), { composite: true }) async icrc1_supported_standards(): Promise { - return await call(this.icrcPrincipal, 'icrc1_supported_standards', { - returnIdlType: IDL.Vec(SupportedStandard) - }); + return await call( + this.icrcPrincipal, + 'icrc1_supported_standards', + { + returnIdlType: IDL.Vec(SupportedStandard) + } + ); } @update([ApproveArgs], ApproveResult) async icrc2_approve(approveArgs: ApproveArgs): Promise { - return await call(this.icrcPrincipal, 'icrc2_approve', { - paramIdlTypes: [ApproveArgs], - returnIdlType: ApproveResult, - args: [approveArgs] - }); + return await call<[ApproveArgs], ApproveResult>( + this.icrcPrincipal, + 'icrc2_approve', + { + paramIdlTypes: [ApproveArgs], + returnIdlType: ApproveResult, + args: [approveArgs] + } + ); } @update([TransferFromArgs], TransferFromResult) async icrc2_transfer_from( transferFromArgs: TransferFromArgs ): Promise { - return await call(this.icrcPrincipal, 'icrc2_transfer_from', { - paramIdlTypes: [TransferFromArgs], - returnIdlType: TransferFromResult, - args: [transferFromArgs] - }); + return await call<[TransferFromArgs], TransferFromResult>( + this.icrcPrincipal, + 'icrc2_transfer_from', + { + paramIdlTypes: [TransferFromArgs], + returnIdlType: TransferFromResult, + args: [transferFromArgs] + } + ); } @update([AllowanceArgs], AllowanceResult) async icrc2_allowance( allowanceArgs: AllowanceArgs ): Promise { - return await call(this.icrcPrincipal, 'icrc2_allowance', { - paramIdlTypes: [AllowanceArgs], - returnIdlType: AllowanceResult, - args: [allowanceArgs] - }); + return await call<[AllowanceArgs], AllowanceResult>( + this.icrcPrincipal, + 'icrc2_allowance', + { + paramIdlTypes: [AllowanceArgs], + returnIdlType: AllowanceResult, + args: [allowanceArgs] + } + ); } } diff --git a/examples/stable/test/end_to_end/candid_rpc/ledger_canister/src/ledger_canister/index.ts b/examples/stable/test/end_to_end/candid_rpc/ledger_canister/src/ledger_canister/index.ts index e8a8add7ca..da5ddbf228 100644 --- a/examples/stable/test/end_to_end/candid_rpc/ledger_canister/src/ledger_canister/index.ts +++ b/examples/stable/test/end_to_end/candid_rpc/ledger_canister/src/ledger_canister/index.ts @@ -9,6 +9,7 @@ import { NameResult, QueryBlocksResponse, SymbolResult, + TimeStamp, Tokens, TransferArgs, TransferFee, @@ -29,84 +30,108 @@ export default class { fee: bigint, createdAtTime: [bigint] | [] ): Promise { - const created_at_time = + const created_at_time: [TimeStamp] | [] = createdAtTime.length === 0 ? [] : [{ timestamp_nanos: createdAtTime[0] }]; - return await call(this.icpCanisterPrincipal, 'transfer', { - paramIdlTypes: [TransferArgs], - returnIdlType: TransferResult, - args: [ - { - memo: 0n, - amount: { - e8s: amount - }, - fee: { - e8s: fee - }, - from_subaccount: [], - to: binaryAddressFromAddress(to), - created_at_time - } - ] - }); + return await call<[TransferArgs], TransferResult>( + this.icpCanisterPrincipal, + 'transfer', + { + paramIdlTypes: [TransferArgs], + returnIdlType: TransferResult, + args: [ + { + memo: 0n, + amount: { + e8s: amount + }, + fee: { + e8s: fee + }, + from_subaccount: [], + to: binaryAddressFromAddress(to), + created_at_time + } + ] + } + ); } @update([IDL.Text], Tokens) async getAccountBalance(address: string): Promise { - return await call(this.icpCanisterPrincipal, 'account_balance', { - paramIdlTypes: [AccountBalanceArgs], - returnIdlType: Tokens, - args: [ - { - account: binaryAddressFromAddress(address) - } - ] - }); + return await call<[AccountBalanceArgs], Tokens>( + this.icpCanisterPrincipal, + 'account_balance', + { + paramIdlTypes: [AccountBalanceArgs], + returnIdlType: Tokens, + args: [ + { + account: binaryAddressFromAddress(address) + } + ] + } + ); } @update([], TransferFee) async getTransferFee(): Promise { - return await call(this.icpCanisterPrincipal, 'transfer_fee', { - paramIdlTypes: [TransferFeeArg], - returnIdlType: TransferFee, - args: [{}] - }); + return await call<[TransferFeeArg], TransferFee>( + this.icpCanisterPrincipal, + 'transfer_fee', + { + paramIdlTypes: [TransferFeeArg], + returnIdlType: TransferFee, + args: [{}] + } + ); } @update([GetBlocksArgs], QueryBlocksResponse) async getBlocks( getBlocksArgs: GetBlocksArgs ): Promise { - return await call(this.icpCanisterPrincipal, 'query_blocks', { - paramIdlTypes: [GetBlocksArgs], - returnIdlType: QueryBlocksResponse, - args: [getBlocksArgs] - }); + return await call<[GetBlocksArgs], QueryBlocksResponse>( + this.icpCanisterPrincipal, + 'query_blocks', + { + paramIdlTypes: [GetBlocksArgs], + returnIdlType: QueryBlocksResponse, + args: [getBlocksArgs] + } + ); } @update([], IDL.Text) async getSymbol(): Promise { - const symbolResult = await call(this.icpCanisterPrincipal, 'symbol', { - returnIdlType: SymbolResult - }); + const symbolResult = await call( + this.icpCanisterPrincipal, + 'symbol', + { + returnIdlType: SymbolResult + } + ); return symbolResult.symbol; } @update([], IDL.Text) async getName(): Promise { - const nameResult = await call(this.icpCanisterPrincipal, 'name', { - returnIdlType: NameResult - }); + const nameResult = await call( + this.icpCanisterPrincipal, + 'name', + { + returnIdlType: NameResult + } + ); return nameResult.name; } @update([], IDL.Nat32) async getDecimals(): Promise { - const decimalsResult = await call( + const decimalsResult = await call( this.icpCanisterPrincipal, 'decimals', { returnIdlType: DecimalsResult } @@ -117,9 +142,13 @@ export default class { @update([], Archives) async getArchives(): Promise { - return await call(this.icpCanisterPrincipal, 'archives', { - returnIdlType: Archives - }); + return await call( + this.icpCanisterPrincipal, + 'archives', + { + returnIdlType: Archives + } + ); } @query([IDL.Principal], IDL.Text) diff --git a/examples/stable/test/end_to_end/candid_rpc/motoko_examples/whoami/src/index.ts b/examples/stable/test/end_to_end/candid_rpc/motoko_examples/whoami/src/index.ts index 08698838ef..eea2115526 100644 --- a/examples/stable/test/end_to_end/candid_rpc/motoko_examples/whoami/src/index.ts +++ b/examples/stable/test/end_to_end/candid_rpc/motoko_examples/whoami/src/index.ts @@ -57,7 +57,7 @@ class WhoAmI { // Return the principal identifier of this canister. @update([], IDL.Principal) async id(): Promise { - return await call(canisterSelf(), 'whoami', { + return await call(canisterSelf(), 'whoami', { returnIdlType: IDL.Principal }); } diff --git a/examples/stable/test/end_to_end/candid_rpc/notify_raw/src/canister1/index.ts b/examples/stable/test/end_to_end/candid_rpc/notify_raw/src/canister1/index.ts index 28c02ff1e9..8b912bd24e 100644 --- a/examples/stable/test/end_to_end/candid_rpc/notify_raw/src/canister1/index.ts +++ b/examples/stable/test/end_to_end/candid_rpc/notify_raw/src/canister1/index.ts @@ -1,17 +1,18 @@ -import { candidEncode, notify, Principal, trap, update } from 'azle'; +import { call, Principal, trap, update } from 'azle'; export default class { @update - sendNotification(): void { - return notify( + async sendNotification(): Promise { + return await call( Principal.fromText( process.env.CANISTER2_PRINCIPAL ?? trap('process.env.CANISTER2_PRINCIPAL is undefined') ), 'receiveNotification', { - raw: Uint8Array.from(candidEncode('()')), - cycles: 0n + cycles: 0n, + oneway: true, + raw: true } ); } diff --git a/examples/stable/test/end_to_end/candid_rpc/outgoing_http_requests/src/index.ts b/examples/stable/test/end_to_end/candid_rpc/outgoing_http_requests/src/index.ts index d1bc3afa3d..14eb76852c 100644 --- a/examples/stable/test/end_to_end/candid_rpc/outgoing_http_requests/src/index.ts +++ b/examples/stable/test/end_to_end/candid_rpc/outgoing_http_requests/src/index.ts @@ -51,11 +51,11 @@ export default class { @update([], http_request_result, { manual: true }) async xkcdRaw(): Promise { - const httpResponse = await call( + const httpResponse = await call( Principal.fromText('aaaaa-aa'), 'http_request', { - raw: candidEncode(` + args: candidEncode(` ( record { url = "https://xkcd.com/642/info.0.json"; @@ -67,7 +67,8 @@ export default class { } ) `), - cycles: 50_000_000n + cycles: 50_000_000n, + raw: true } ); diff --git a/examples/stable/test/end_to_end/candid_rpc/recursion/src/recursion/index.ts b/examples/stable/test/end_to_end/candid_rpc/recursion/src/recursion/index.ts index 86545b1f12..ea324d2573 100644 --- a/examples/stable/test/end_to_end/candid_rpc/recursion/src/recursion/index.ts +++ b/examples/stable/test/end_to_end/candid_rpc/recursion/src/recursion/index.ts @@ -250,10 +250,14 @@ export default class { async testRecServiceCall( myFullCanister: MyFullCanister ): Promise { - return await call(myFullCanister, 'myQuery', { - paramIdlTypes: [MyFullCanister], - returnIdlType: MyFullCanister, - args: [myFullCanister] - }); + return await call<[MyFullCanister], MyFullCanister>( + myFullCanister, + 'myQuery', + { + paramIdlTypes: [MyFullCanister], + returnIdlType: MyFullCanister, + args: [myFullCanister] + } + ); } } diff --git a/examples/stable/test/end_to_end/candid_rpc/rejections/src/rejections/index.ts b/examples/stable/test/end_to_end/candid_rpc/rejections/src/rejections/index.ts index 0cb4047e98..68a5bd6076 100644 --- a/examples/stable/test/end_to_end/candid_rpc/rejections/src/rejections/index.ts +++ b/examples/stable/test/end_to_end/candid_rpc/rejections/src/rejections/index.ts @@ -3,7 +3,7 @@ import { call, IDL, msgRejectCode, msgRejectMsg, update } from 'azle'; export default class { @update([], IDL.Nat32) async getRejectionCodeNoError(): Promise { - await call(getSomeCanisterPrincipal(), 'accept', { + await call(getSomeCanisterPrincipal(), 'accept', { returnIdlType: IDL.Bool }); @@ -13,7 +13,10 @@ export default class { @update([], IDL.Nat32) async getRejectionCodeDestinationInvalid(): Promise { try { - await call('rkp4c-7iaaa-aaaaa-aaaca-cai', 'method'); + await call( + 'rkp4c-7iaaa-aaaaa-aaaca-cai', + 'method' + ); } catch { // continue regardless of error } @@ -24,7 +27,7 @@ export default class { @update([], IDL.Nat32) async getRejectionCodeCanisterReject(): Promise { try { - await call(getSomeCanisterPrincipal(), 'reject', { + await call<[string], void>(getSomeCanisterPrincipal(), 'reject', { paramIdlTypes: [IDL.Text], args: ['reject'] }); @@ -38,7 +41,7 @@ export default class { @update([], IDL.Nat32) async getRejectionCodeCanisterError(): Promise { try { - await call(getSomeCanisterPrincipal(), 'error'); + await call(getSomeCanisterPrincipal(), 'error'); } catch { // continue regardless of error } @@ -49,7 +52,7 @@ export default class { @update([IDL.Text], IDL.Text) async getRejectionMessage(message: string): Promise { try { - await call(getSomeCanisterPrincipal(), 'reject', { + await call<[string], void>(getSomeCanisterPrincipal(), 'reject', { paramIdlTypes: [IDL.Text], args: [message] }); diff --git a/examples/stable/test/end_to_end/candid_rpc/timers/src/timers.ts b/examples/stable/test/end_to_end/candid_rpc/timers/src/timers.ts index 1be40772f7..16ecbc0a24 100644 --- a/examples/stable/test/end_to_end/candid_rpc/timers/src/timers.ts +++ b/examples/stable/test/end_to_end/candid_rpc/timers/src/timers.ts @@ -128,7 +128,7 @@ async function repeatCrossCanisterTimerCallback( } async function getRandomness(): Promise { - return await call('aaaaa-aa', 'raw_rand', { + return await call('aaaaa-aa', 'raw_rand', { returnIdlType: IDL.Vec(IDL.Nat8) }); } diff --git a/examples/stable/test/property/ic_api/cycles/src/intermediary/index.ts b/examples/stable/test/property/ic_api/cycles/src/intermediary/index.ts index c7f5fec191..4bac7b289c 100644 --- a/examples/stable/test/property/ic_api/cycles/src/intermediary/index.ts +++ b/examples/stable/test/property/ic_api/cycles/src/intermediary/index.ts @@ -15,10 +15,14 @@ export default class { @update([IDL.Nat64], CyclesResult) async sendAllCycles(amount: bigint): Promise { - const result = await call(this.cyclesPrincipal, 'receiveAllCycles', { - returnIdlType: CyclesResult, - cycles: amount - }); + const result = await call( + this.cyclesPrincipal, + 'receiveAllCycles', + { + returnIdlType: CyclesResult, + cycles: amount + } + ); return { ...result, cyclesRefunded: msgCyclesRefunded() }; } @@ -27,7 +31,7 @@ export default class { amountToSend: bigint, amountToAccept: bigint ): Promise { - const result = await call( + const result = await call<[bigint], CyclesResult>( this.cyclesPrincipal, 'receiveVariableCycles', { @@ -42,10 +46,14 @@ export default class { @update([IDL.Nat64], CyclesResult) async sendNoCycles(amount: bigint): Promise { - const result = await call(this.cyclesPrincipal, 'receiveNoCycles', { - returnIdlType: CyclesResult, - cycles: amount - }); + const result = await call( + this.cyclesPrincipal, + 'receiveNoCycles', + { + returnIdlType: CyclesResult, + cycles: amount + } + ); return { ...result, cyclesRefunded: msgCyclesRefunded() }; } @@ -54,7 +62,7 @@ export default class { amount: bigint, numChunks: bigint ): Promise { - const result = await call( + const result = await call<[bigint], CyclesResult>( this.cyclesPrincipal, 'receiveCyclesByChunk', { @@ -77,16 +85,20 @@ export default class { @update([IDL.Nat64], IDL.Bool) async assertMsgCyclesAcceptTypes(amount: bigint): Promise { - return await call(this.cyclesPrincipal, 'assertMsgCyclesAcceptTypes', { - paramIdlTypes: [IDL.Nat64], - returnIdlType: IDL.Bool, - args: [amount] - }); + return await call<[bigint], boolean>( + this.cyclesPrincipal, + 'assertMsgCyclesAcceptTypes', + { + paramIdlTypes: [IDL.Nat64], + returnIdlType: IDL.Bool, + args: [amount] + } + ); } @update([], IDL.Bool) async assertMsgCyclesAvailableTypes(): Promise { - return await call( + return await call( this.cyclesPrincipal, 'assertMsgCyclesAvailableTypes', { @@ -97,11 +109,15 @@ export default class { @update([IDL.Nat64], IDL.Bool) async assertMsgCyclesRefundedTypes(amount: bigint): Promise { - await call(this.cyclesPrincipal, 'assertMsgCyclesAcceptTypes', { - paramIdlTypes: [IDL.Nat64], - returnIdlType: IDL.Bool, - args: [amount] - }); + await call<[bigint], boolean>( + this.cyclesPrincipal, + 'assertMsgCyclesAcceptTypes', + { + paramIdlTypes: [IDL.Nat64], + returnIdlType: IDL.Bool, + args: [amount] + } + ); // NOTE: if there is no cross canister call, msgCyclesRefunded() cannot be called type _AssertReturnType = AssertType< NotAnyAndExact, bigint> diff --git a/examples/stable/test/property/ic_api/in_replicated_execution/src/index.ts b/examples/stable/test/property/ic_api/in_replicated_execution/src/index.ts index cf248ff890..ebb513c702 100644 --- a/examples/stable/test/property/ic_api/in_replicated_execution/src/index.ts +++ b/examples/stable/test/property/ic_api/in_replicated_execution/src/index.ts @@ -136,9 +136,13 @@ export default class { @update([], IDL.Bool) async getQueryInReplicatedModeIsInReplicatedExecution(): Promise { - return await call(canisterSelf(), 'getQueryIsInReplicatedExecution', { - returnIdlType: IDL.Bool - }); + return await call( + canisterSelf(), + 'getQueryIsInReplicatedExecution', + { + returnIdlType: IDL.Bool + } + ); } @query([], IDL.Bool, { composite: true }) diff --git a/examples/stable/test/property/ic_api/msg_reject_code/src/caller/index.ts b/examples/stable/test/property/ic_api/msg_reject_code/src/caller/index.ts index 9a5d677266..81079e8a8f 100644 --- a/examples/stable/test/property/ic_api/msg_reject_code/src/caller/index.ts +++ b/examples/stable/test/property/ic_api/msg_reject_code/src/caller/index.ts @@ -22,7 +22,10 @@ export default class { @update([], IDL.Bool) async assertTypes(): Promise { type _AssertReturnType = AssertType< - NotAnyAndExact, number> + NotAnyAndExact< + ReturnType, + 0 | 1 | 2 | 3 | 4 | 5 | 6 + > >; const throwErrorRejectCode = await getThrowErrorRejectCode( this.rejectorPrincipal @@ -44,7 +47,7 @@ async function getThrowErrorRejectCode( rejectorPrincipal: string ): Promise { try { - await call(rejectorPrincipal, 'throwError', { + await call(rejectorPrincipal, 'throwError', { returnIdlType: IDL.Text }); } catch { @@ -57,7 +60,7 @@ async function getRejectWithMessageRejectCode( rejectorPrincipal: string ): Promise { try { - await call(rejectorPrincipal, 'rejectWithMessage', { + await call(rejectorPrincipal, 'rejectWithMessage', { returnIdlType: IDL.Text }); } catch { @@ -69,7 +72,7 @@ async function getRejectWithMessageRejectCode( async function getNoErrorRejectCode( rejectorPrincipal: string ): Promise { - await call(rejectorPrincipal, 'noError'); + await call(rejectorPrincipal, 'noError'); return msgRejectCode(); } diff --git a/examples/stable/test/property/ic_api/msg_reject_msg/src/caller/index.ts b/examples/stable/test/property/ic_api/msg_reject_msg/src/caller/index.ts index 75777120b8..0b4b38b646 100644 --- a/examples/stable/test/property/ic_api/msg_reject_msg/src/caller/index.ts +++ b/examples/stable/test/property/ic_api/msg_reject_msg/src/caller/index.ts @@ -26,7 +26,7 @@ async function getRejectMessage( message: string ): Promise { try { - await call(rejectorPrincipal, 'echoReject', { + await call<[string], string>(rejectorPrincipal, 'echoReject', { paramIdlTypes: [IDL.Text], returnIdlType: IDL.Text, args: [message] diff --git a/src/build/experimental/commands/compile/wasm_binary/rust/experimental_canister_template/src/chunk.rs b/src/build/experimental/commands/compile/wasm_binary/rust/experimental_canister_template/src/chunk.rs index 78e9f70558..9e745c46d4 100644 --- a/src/build/experimental/commands/compile/wasm_binary/rust/experimental_canister_template/src/chunk.rs +++ b/src/build/experimental/commands/compile/wasm_binary/rust/experimental_canister_template/src/chunk.rs @@ -2,6 +2,6 @@ pub async fn chunk() { let id = ic_cdk::id(); let method = "_azle_chunk"; - let args_raw = [68, 73, 68, 76, 0, 0]; // '()' pre encoded + let args_raw = [68, 73, 68, 76, 0, 0]; // pre-encoded Candid empty params let _ = ic_cdk::api::call::call_raw128(id, method, args_raw, 0).await; } diff --git a/src/build/experimental/commands/compile/wasm_binary/rust/experimental_canister_template/src/ic/call_raw.rs b/src/build/experimental/commands/compile/wasm_binary/rust/experimental_canister_template/src/ic/call_raw.rs index eb4de194fd..339b0d2911 100644 --- a/src/build/experimental/commands/compile/wasm_binary/rust/experimental_canister_template/src/ic/call_raw.rs +++ b/src/build/experimental/commands/compile/wasm_binary/rust/experimental_canister_template/src/ic/call_raw.rs @@ -5,13 +5,19 @@ use crate::run_event_loop; pub struct NativeFunction; impl JsFn for NativeFunction { fn call(context: &mut Context, _this_val: JsValue, argv: &[JsValue]) -> JsValue { - let promise_id = if let JsValue::String(js_string) = argv.get(0).unwrap() { + let global_resolve_id = if let JsValue::String(js_string) = argv.get(0).unwrap() { js_string.to_string() } else { panic!("conversion from JsValue to JsString failed") }; - let canister_id_bytes = if let JsValue::ArrayBuffer(js_array_buffer) = argv.get(1).unwrap() + let global_reject_id = if let JsValue::String(js_string) = argv.get(1).unwrap() { + js_string.to_string() + } else { + panic!("conversion from JsValue to JsString failed") + }; + + let canister_id_bytes = if let JsValue::ArrayBuffer(js_array_buffer) = argv.get(2).unwrap() { js_array_buffer.to_vec() } else { @@ -19,19 +25,19 @@ impl JsFn for NativeFunction { }; let canister_id = candid::Principal::from_slice(&canister_id_bytes); - let method = if let JsValue::String(js_string) = argv.get(2).unwrap() { + let method = if let JsValue::String(js_string) = argv.get(3).unwrap() { js_string.to_string() } else { panic!("conversion from JsValue to JsString failed") }; - let args_raw = if let JsValue::ArrayBuffer(js_array_buffer) = argv.get(3).unwrap() { + let args_raw = if let JsValue::ArrayBuffer(js_array_buffer) = argv.get(4).unwrap() { js_array_buffer.to_vec() } else { panic!("conversion from JsValue to JsArrayBuffer failed") }; - let payment_string = if let JsValue::String(js_string) = argv.get(4).unwrap() { + let payment_string = if let JsValue::String(js_string) = argv.get(5).unwrap() { js_string.to_string() } else { panic!("conversion from JsValue to JsString failed") @@ -52,14 +58,14 @@ impl JsFn for NativeFunction { let _cleanup = scopeguard::guard((), |_| { let global = context_clone_cleanup.get_global(); - let reject_id = format!("_reject_{}", promise_id); - let resolve_id = format!("_resolve_{}", promise_id); - - let reject_callbacks = global.get("_azleRejectCallbacks"); let resolve_callbacks = global.get("_azleResolveCallbacks"); + let reject_callbacks = global.get("_azleRejectCallbacks"); - reject_callbacks.to_obj().unwrap().delete(&reject_id); - resolve_callbacks.to_obj().unwrap().delete(&resolve_id); + resolve_callbacks + .to_obj() + .unwrap() + .delete(&global_resolve_id); + reject_callbacks.to_obj().unwrap().delete(&global_reject_id); }); let call_result = @@ -75,15 +81,18 @@ impl JsFn for NativeFunction { (true, candid_bytes_js_value) } Err(err) => { - let err_js_value: JsValue = context_clone + let mut err_object = context_clone .new_error(&format!( - "Rejection code {rejection_code}, {error_message}", - rejection_code = (err.0 as i32).to_string(), - error_message = err.1 + "The inter-canister call failed with reject code {}: {}", + err.0 as i32, err.1 )) - .into(); + .to_obj() + .unwrap(); + + err_object.set("rejectCode", (err.0 as i32).into()); + err_object.set("rejectMessage", context_clone.new_string(&err.1).into()); - (false, err_js_value) + (false, err_object.into()) } }; @@ -92,7 +101,7 @@ impl JsFn for NativeFunction { .get("_azleResolveCallbacks") .to_obj() .unwrap() - .get(format!("_resolve_{promise_id}").as_str()) + .get(&global_resolve_id) .to_function() .unwrap(); @@ -114,7 +123,7 @@ impl JsFn for NativeFunction { .get("_azleRejectCallbacks") .to_obj() .unwrap() - .get(format!("_reject_{promise_id}").as_str()) + .get(&global_reject_id) .to_function() .unwrap(); diff --git a/src/build/experimental/commands/compile/wasm_binary/rust/experimental_canister_template/src/ic/msg_reject_code.rs b/src/build/experimental/commands/compile/wasm_binary/rust/experimental_canister_template/src/ic/msg_reject_code.rs index d51bdd4346..629e0d8014 100644 --- a/src/build/experimental/commands/compile/wasm_binary/rust/experimental_canister_template/src/ic/msg_reject_code.rs +++ b/src/build/experimental/commands/compile/wasm_binary/rust/experimental_canister_template/src/ic/msg_reject_code.rs @@ -2,7 +2,7 @@ use wasmedge_quickjs::{Context, JsFn, JsValue}; pub struct NativeFunction; impl JsFn for NativeFunction { - fn call(context: &mut Context, _this_val: JsValue, _argv: &[JsValue]) -> JsValue { + fn call(_context: &mut Context, _this_val: JsValue, _argv: &[JsValue]) -> JsValue { let reject_code = ic_cdk::api::call::reject_code(); let reject_code_number = match reject_code { @@ -15,6 +15,6 @@ impl JsFn for NativeFunction { ic_cdk::api::call::RejectionCode::Unknown => 6, }; - context.new_string(&reject_code_number.to_string()).into() + reject_code_number.into() } } diff --git a/src/build/stable/commands/compile/wasm_binary/rust/stable_canister_template/src/chunk.rs b/src/build/stable/commands/compile/wasm_binary/rust/stable_canister_template/src/chunk.rs index cc009cbeee..fcb4ddcfaf 100644 --- a/src/build/stable/commands/compile/wasm_binary/rust/stable_canister_template/src/chunk.rs +++ b/src/build/stable/commands/compile/wasm_binary/rust/stable_canister_template/src/chunk.rs @@ -4,6 +4,6 @@ use ic_cdk::{api::call::call_raw128, id}; pub async fn chunk() { let id = id(); let method = "_azle_chunk"; - let args_raw = [68, 73, 68, 76, 0, 0]; // '()' pre encoded + let args_raw = [68, 73, 68, 76, 0, 0]; // pre-encoded Candid empty params let _ = call_raw128(id, method, args_raw, 0).await; } diff --git a/src/build/stable/commands/compile/wasm_binary/rust/stable_canister_template/src/ic/call_raw.rs b/src/build/stable/commands/compile/wasm_binary/rust/stable_canister_template/src/ic/call_raw.rs index c2b89c828b..3c244fd467 100644 --- a/src/build/stable/commands/compile/wasm_binary/rust/stable_canister_template/src/ic/call_raw.rs +++ b/src/build/stable/commands/compile/wasm_binary/rust/stable_canister_template/src/ic/call_raw.rs @@ -29,7 +29,8 @@ pub fn get_function(ctx: Ctx) -> QuickJsResult { Function::new( ctx.clone(), - move |promise_id: String, + move |global_resolve_id: String, + global_reject_id: String, canister_id_bytes: TypedArray, method: String, args_raw: TypedArray, @@ -57,7 +58,7 @@ pub fn get_function(ctx: Ctx) -> QuickJsResult { // Even during a trap, the IC will ensure that the closure runs in its own call // thus allowing us to recover from a trap and persist that state let _cleanup = scopeguard::guard((), |_| { - let result = cleanup(ctx.clone(), &promise_id); + let result = cleanup(ctx.clone(), &global_resolve_id, &global_reject_id); if let Err(e) = result { trap(&format!("Azle CallRawCleanupError: {e}")); @@ -66,7 +67,12 @@ pub fn get_function(ctx: Ctx) -> QuickJsResult { let call_result = call_raw128(canister_id, &method, args_raw, payment).await; - let result = resolve_or_reject(ctx.clone(), &call_result, &promise_id); + let result = resolve_or_reject( + ctx.clone(), + &call_result, + &global_resolve_id, + &global_reject_id, + ); if let Err(e) = result { trap(&format!("Azle CallRawError: {e}")); @@ -78,17 +84,18 @@ pub fn get_function(ctx: Ctx) -> QuickJsResult { ) } -fn cleanup(ctx: Ctx, promise_id: &str) -> Result<(), Box> { - let reject_id = format!("_reject_{}", promise_id); - let resolve_id = format!("_resolve_{}", promise_id); - +fn cleanup( + ctx: Ctx, + global_resolve_id: &str, + global_reject_id: &str, +) -> Result<(), Box> { let globals = ctx.clone().globals(); - let reject_ids = globals.get::<_, Object>("_azleRejectCallbacks")?; - let resolve_ids = globals.get::<_, Object>("_azleResolveCallbacks")?; + let resolve_callbacks = globals.get::<_, Object>("_azleResolveCallbacks")?; + let reject_callbacks = globals.get::<_, Object>("_azleRejectCallbacks")?; - reject_ids.remove(&reject_id)?; - resolve_ids.remove(&resolve_id)?; + resolve_callbacks.remove(global_resolve_id)?; + reject_callbacks.remove(global_reject_id)?; Ok(()) } @@ -96,10 +103,16 @@ fn cleanup(ctx: Ctx, promise_id: &str) -> Result<(), Box> { fn resolve_or_reject<'a>( ctx: Ctx<'a>, call_result: &Result, (RejectionCode, String)>, - promise_id: &str, + global_resolve_id: &str, + global_reject_id: &str, ) -> Result<(), Box> { let (should_resolve, js_value) = prepare_js_value(ctx.clone(), &call_result)?; - let callback = get_callback(ctx.clone(), &promise_id, should_resolve)?; + let callback = get_callback( + ctx.clone(), + should_resolve, + global_resolve_id, + global_reject_id, + )?; quickjs_call_with_error_handling(ctx.clone(), callback, (js_value,))?; @@ -118,24 +131,34 @@ fn prepare_js_value<'a>( Ok((true, candid_bytes_js_value)) } Err(err) => { - let err_js_value = Exception::from_message( + let err_js_object = Exception::from_message( ctx.clone(), - &format!("Rejection code {}, {}", (err.0 as i32).to_string(), err.1), - ) - .into_js(&ctx)?; + &format!( + "The inter-canister call failed with reject code {}: {}", + err.0 as i32, &err.1 + ), + )?; + + err_js_object.set("rejectCode", err.0 as i32)?; + err_js_object.set("rejectMessage", &err.1)?; - Ok((false, err_js_value)) + Ok((false, err_js_object.into_js(&ctx)?)) } } } fn get_callback<'a>( ctx: Ctx<'a>, - promise_id: &str, should_resolve: bool, + global_resolve_id: &str, + global_reject_id: &str, ) -> Result, Box> { let global_object = get_resolve_or_reject_global_object(ctx.clone(), should_resolve)?; - let callback_name = get_resolve_or_reject_callback_name(&promise_id, should_resolve); + let callback_name = if should_resolve { + global_resolve_id + } else { + global_reject_id + }; Ok(global_object.get(callback_name)?) } @@ -152,11 +175,3 @@ fn get_resolve_or_reject_global_object( Ok(globals.get("_azleRejectCallbacks")?) } } - -fn get_resolve_or_reject_callback_name(promise_id: &str, should_resolve: bool) -> String { - if should_resolve { - format!("_resolve_{promise_id}") - } else { - format!("_reject_{promise_id}") - } -} diff --git a/src/build/stable/commands/compile/wasm_binary/rust/stable_canister_template/src/ic/notify_raw.rs b/src/build/stable/commands/compile/wasm_binary/rust/stable_canister_template/src/ic/notify_raw.rs index e9b8297b0d..40a3c85fdb 100644 --- a/src/build/stable/commands/compile/wasm_binary/rust/stable_canister_template/src/ic/notify_raw.rs +++ b/src/build/stable/commands/compile/wasm_binary/rust/stable_canister_template/src/ic/notify_raw.rs @@ -29,14 +29,17 @@ pub fn get_function(ctx: Ctx) -> Result { match notify_result { Ok(_) => Undefined.into_js(&ctx), Err(err) => { - let err_string = format!( - "Rejection code {rejection_code}", - rejection_code = (err as i32).to_string() - ); - let err_js_value = - Exception::from_message(ctx.clone(), &err_string).into_js(&ctx)?; + let err_js_object = Exception::from_message( + ctx.clone(), + &format!( + "The inter-canister call failed with reject code {}", + err as i32 + ), + )?; - Ok(err_js_value) + err_js_object.set("rejectCode", err as i32)?; + + Ok(err_js_object.into_js(&ctx)?) } } }, diff --git a/src/lib/experimental/candid/types/reference/service/canister_function/index.ts b/src/lib/experimental/candid/types/reference/service/canister_function/index.ts index d53e653005..3dee9aeca6 100644 --- a/src/lib/experimental/candid/types/reference/service/canister_function/index.ts +++ b/src/lib/experimental/candid/types/reference/service/canister_function/index.ts @@ -7,7 +7,7 @@ import { MethodMeta } from '../../../../../../../build/stable/utils/types'; import { CanisterMethodMode } from '../../../../../../stable/execute_with_candid_serde'; -import { call, notify } from '../../../../../../stable/ic_apis'; +import { call } from '../../../../../../stable/ic_apis'; import { CanisterMethodInfo } from '../../../../../canister_methods/types/canister_method_info'; import { Callbacks } from '../../../../../globals'; import { CandidType, Parent, toIdlTypeArray } from '../../../../index'; @@ -262,28 +262,25 @@ function serviceCall( paramCandidTypes: CandidType[], returnCandidType: CandidType ): ServiceCall { - return ( - isNotify: boolean, - cycles: bigint, - args: any[] - ): void | Promise => { + return (isNotify: boolean, cycles: bigint, args: any[]): Promise => { const encodedArgs = encode(paramCandidTypes, args); if (isNotify === true) { - return notify(canisterId, methodName, { - args, + return call(canisterId, methodName, { + args: encodedArgs, cycles, - raw: encodedArgs + oneway: true, + raw: true }); } else { return (async (): Promise => { - const encodedResult = await call( + const encodedResult = await call( canisterId, methodName, { - args, + args: encodedArgs, cycles, - raw: encodedArgs + raw: true } ); diff --git a/src/lib/experimental/fetch/icp.ts b/src/lib/experimental/fetch/icp.ts index 7393f4a125..1de8dd26da 100644 --- a/src/lib/experimental/fetch/icp.ts +++ b/src/lib/experimental/fetch/icp.ts @@ -51,12 +51,13 @@ export async function fetchIcp( const canisterPrincipal = Principal.fromText(canisterId); - const result = await call( + const result = await call( canisterPrincipal, canisterMethod, { - raw: argsRaw, - cycles: BigInt(cycles ?? 0) + args: argsRaw, + cycles: BigInt(cycles ?? 0), + raw: true } ); diff --git a/src/lib/experimental/ic/azle_ic_experimental.ts b/src/lib/experimental/ic/azle_ic_experimental.ts index 0a5dd85b4d..ecd94c5db7 100644 --- a/src/lib/experimental/ic/azle_ic_experimental.ts +++ b/src/lib/experimental/ic/azle_ic_experimental.ts @@ -1,3 +1,5 @@ +import { RejectCode } from '../../stable/ic_apis/msg_reject_code'; + /** * The interface for our rust methods it slightly different than the interface * we expose to the users. This is the interface for the rust functions. @@ -5,7 +7,8 @@ export type AzleIcExperimental = { msgArgData: () => ArrayBuffer; callRaw: ( - promiseId: string, + globalResolveId: string, + globalRejectId: string, canisterIdBytes: ArrayBuffer, method: string, argsRaw: ArrayBuffer, @@ -33,7 +36,7 @@ export type AzleIcExperimental = { paymentString: string ) => void; performanceCounter: (counterType: string) => string; - msgRejectCode: () => string; + msgRejectCode: () => RejectCode; msgReply: (bytes: ArrayBuffer) => void; setCertifiedData: (dataBytes: ArrayBuffer) => void; setTimer: (delayString: string, timerCallbackId: string) => string; @@ -52,12 +55,6 @@ export type AzleIcExperimental = { msgReject: (message: string) => void; msgRejectMsg: () => string; trap: (message: string) => never; - // These calls are intercepted by our IC object and redirected to their - // corresponding raw version. The rust version is never called, we don't - // have enough info about types to do so - call: () => never; - notify: () => never; - reply: () => never; // Stable B Tree Map Functions stableBTreeMapInit: (memoryId: string) => void; stableBTreeMapContainsKey: ( diff --git a/src/lib/stable/ic_apis/azle_ic_stable.ts b/src/lib/stable/ic_apis/azle_ic_stable.ts index 6a52a6026c..066dbb8166 100644 --- a/src/lib/stable/ic_apis/azle_ic_stable.ts +++ b/src/lib/stable/ic_apis/azle_ic_stable.ts @@ -1,3 +1,5 @@ +import { RejectCode } from './msg_reject_code'; + /** * The interface for our rust methods it slightly different than the interface * we expose to the users. This is the interface for the rust functions. @@ -5,7 +7,8 @@ export type AzleIcStable = { msgArgData: () => Uint8Array; callRaw: ( - promiseId: string, + globalResolveId: string, + globalRejectId: string, canisterIdBytes: Uint8Array, method: string, argsRaw: Uint8Array, @@ -32,7 +35,7 @@ export type AzleIcStable = { cyclesString: string ) => void; performanceCounter: (counterType: number) => bigint; - msgRejectCode: () => number; + msgRejectCode: () => RejectCode; msgReply: (bytes: Uint8Array) => void; setCertifiedData: (dataBytes: Uint8Array) => void; setTimer: (delay: string, timerCallbackId: string) => bigint; @@ -48,12 +51,6 @@ export type AzleIcStable = { msgReject: (message: string) => void; msgRejectMsg: () => string; trap: (message: string) => never; - // These calls are intercepted by our IC object and redirected to their - // corresponding raw version. The rust version is never called, we don't - // have enough info about types to do so - call: () => never; - notify: () => never; - reply: () => never; // Stable B Tree Map Functions stableBTreeMapInit: (memoryId: number) => void; stableBTreeMapContainsKey: ( diff --git a/src/lib/stable/ic_apis/call.ts b/src/lib/stable/ic_apis/call.ts index dd84b54793..f9828764f8 100644 --- a/src/lib/stable/ic_apis/call.ts +++ b/src/lib/stable/ic_apis/call.ts @@ -3,24 +3,66 @@ import { Principal } from '@dfinity/principal'; import { v4 } from 'uuid'; import { idlDecode, idlEncode } from '../execute_with_candid_serde'; +import { RejectCode } from './msg_reject_code'; + +type CallOptions = { + /** + * Candid types for encoding the arguments + */ + paramIdlTypes?: IDL.Type[]; + /** + * Candid type for decoding the return value + */ + returnIdlType?: IDL.Type; + /** + * Arguments to pass to the method. An array of JavaScript values for non-raw calls, a Uint8Array for raw calls + */ + args?: Args; + /** + * Number of cycles to attach to the call. Represented as a u128 (max size 2^128 - 1) + */ + cycles?: bigint; + /** + * Instructs the inter-canister call to resolve its promise immediately without waiting for the response. ICP's async mechanism is not invoked + */ + oneway?: boolean; + /** + * Instructs the inter-canister call to accept raw bytes for the arguments and to return raw bytes; skips encoding and decoding + */ + raw?: boolean; + /** + * Not yet implemented. Will allow for best-effort inter-canister calls + */ + timeout?: bigint | null; +}; + +// TODO add the sync property once the ic-cdk v0.18.0 comes out +export interface CallError extends Error { + rejectCode: RejectCode; + rejectMessage?: string; +} /** - * Makes an inter-canister call to invoke a method on another canister. + * Makes an inter-canister call to a method on another canister. * - * @param canisterId - The target canister's ID as a Principal or string + * @param canisterId - The target canister's ID * @param method - The name of the method to call on the target canister * @param options - Configuration options for the call - * @param options.paramIdlTypes - Candid types for the parameters (optional) - * @param options.returnIdlType - Candid type for the return value (optional) - * @param options.args - Arguments to pass to the method (optional) - * @param options.cycles - Number of cycles to attach to the call (optional). Represented as a u128 (max size 2^128 - 1) - * @param options.raw - Raw bytes to send instead of encoded args (optional) - * @returns Promise resolving to the method's return value + * @param options.paramIdlTypes - Candid types for encoding the arguments + * @param options.returnIdlType - Candid type for decoding the return value + * @param options.args - Arguments to pass to the method. An array of JavaScript values for non-raw calls, a Uint8Array for raw calls + * @param options.cycles - Number of cycles to attach to the call. Represented as a u128 (max size 2^128 - 1) + * @param options.oneway - Instructs the inter-canister call to resolve its promise immediately without waiting for the response. ICP's async mechanism is not invoked + * @param options.raw - Instructs the inter-canister call to accept raw bytes for the arguments and to return raw bytes; skips encoding and decoding + * @param options.timeout - Not yet implemented. Will allow for best-effort inter-canister calls + * + * @returns Promise resolving to the target canister method's return value * * @remarks + * + * - By default (if oneway is not set to true) the inter-canister call is asynchronous according to ICP's async mechanism * - Supports both high-level (with IDL types) and low-level (raw bytes) calls - * - Can transfer cycles as part of the call - * - Automatically encodes/decodes arguments and return values + * - Calls are routed based on the target canister's ID, regardless of which subnet the originating and target canisters are deployed to * * - **Call Context**: * - \@update @@ -32,69 +74,140 @@ import { idlDecode, idlEncode } from '../execute_with_candid_serde'; * - after a successful inter-canister await from a composite query * - after an unsuccessful inter-canister await from a composite query */ -export async function call( +export async function call< + Args extends any[] | Uint8Array | undefined, + Return = any +>( canisterId: Principal | string, method: string, - options?: { - paramIdlTypes?: IDL.Type[]; - returnIdlType?: IDL.Type; - args?: Args; - cycles?: bigint; - raw?: Uint8Array; - } + options?: CallOptions ): Promise { - // TODO this should use a Result remember - return new Promise((resolve, reject) => { + if (typeof options?.timeout === 'bigint') { + throw new Error('timeout is not yet implemented'); + } + + const canisterIdBytes = getCanisterIdBytes(canisterId); + const argsRaw = getArgsRaw(options); + const cyclesString = getCyclesString(options); + + if (options?.oneway === true) { + return handleOneWay( + canisterIdBytes, + method, + argsRaw, + cyclesString + ); + } else { + return handleTwoWay( + canisterIdBytes, + method, + argsRaw, + cyclesString, + options?.raw ?? false, + options?.returnIdlType + ); + } +} + +function getCanisterIdBytes(canisterId: Principal | string): Uint8Array { + return typeof canisterId === 'string' + ? Principal.fromText(canisterId).toUint8Array() + : canisterId.toUint8Array(); +} + +function getArgsRaw( + callOptions?: CallOptions +): Uint8Array { + if (callOptions?.raw === true) { + if (callOptions?.args === undefined) { + return new Uint8Array([68, 73, 68, 76, 0, 0]); // pre-encoded Candid empty params + } + + if (callOptions.args instanceof Uint8Array === false) { + throw new Error( + `args must be a Uint8Array. If you did not intend to make a raw call, then consider setting the raw property of the call options to undefined or false` + ); + } + + return callOptions.args; + } else { + if (callOptions?.args instanceof Uint8Array === true) { + throw new Error( + `args must be an array of JavaScript values. If you intended to make a raw call, then consider setting the raw property of the call options to true` + ); + } + if ( - globalThis._azleIcStable === undefined && - globalThis._azleIcExperimental === undefined + callOptions?.paramIdlTypes === undefined && + callOptions?.args === undefined ) { - return undefined; + return new Uint8Array([68, 73, 68, 76, 0, 0]); // pre-encoded Candid empty params } + return idlEncode( + callOptions?.paramIdlTypes ?? [], + callOptions?.args ?? [] + ); + } +} + +function getCyclesString( + options?: CallOptions +): string { + const cycles = options?.cycles ?? 0n; + return cycles.toString(); +} + +function handleOneWay( + canisterIdBytes: Uint8Array, + method: string, + argsRaw: Uint8Array, + cyclesString: string +): Promise { + if (globalThis._azleIcExperimental !== undefined) { + globalThis._azleIcExperimental.notifyRaw( + canisterIdBytes.buffer, + method, + argsRaw.buffer, + cyclesString + ); + } else { + globalThis._azleIcStable.notifyRaw( + canisterIdBytes, + method, + argsRaw, + cyclesString + ); + } + + return Promise.resolve(undefined as Return); +} + +function handleTwoWay( + canisterIdBytes: Uint8Array, + method: string, + argsRaw: Uint8Array, + cyclesString: string, + raw: boolean, + returnIdlType?: IDL.Type +): Promise { + return new Promise((resolve, reject) => { const promiseId = v4(); const globalResolveId = `_resolve_${promiseId}`; const globalRejectId = `_reject_${promiseId}`; - const returnTypeIdl = options?.returnIdlType; - const raw = options?.raw; - - globalThis._azleResolveCallbacks[globalResolveId] = ( - result: Uint8Array | ArrayBuffer - ): void => { - if (raw !== undefined) { - resolve(new Uint8Array(result) as Return); - } else { - const idlType = - returnTypeIdl === undefined ? [] : [returnTypeIdl]; - resolve( - idlDecode(idlType, new Uint8Array(result))[0] as Return - ); - } - }; - - globalThis._azleRejectCallbacks[globalRejectId] = ( - error: any - ): void => { - reject(error); - }; - - const paramIdlTypes = options?.paramIdlTypes ?? []; - const args = options?.args ?? []; - const cycles = options?.cycles ?? 0n; - - const canisterIdPrincipal = - typeof canisterId === 'string' - ? Principal.fromText(canisterId) - : canisterId; - const canisterIdBytes = canisterIdPrincipal.toUint8Array(); - const argsRaw = - raw === undefined ? idlEncode(paramIdlTypes, args) : raw; - const cyclesString = cycles.toString(); + createResolveCallback( + globalResolveId, + resolve, + raw, + returnIdlType + ); + createRejectCallback(globalRejectId, reject); if (globalThis._azleIcExperimental !== undefined) { globalThis._azleIcExperimental.callRaw( - promiseId, + globalResolveId, + globalRejectId, canisterIdBytes.buffer, method, argsRaw.buffer, @@ -102,7 +215,8 @@ export async function call( ); } else { globalThis._azleIcStable.callRaw( - promiseId, + globalResolveId, + globalRejectId, canisterIdBytes, method, argsRaw, @@ -111,3 +225,36 @@ export async function call( } }); } + +function createResolveCallback( + globalResolveId: string, + resolve: (value: Return | PromiseLike) => void, + raw: boolean, + returnIdlType?: IDL.Type +): void { + globalThis._azleResolveCallbacks[globalResolveId] = ( + result: Uint8Array | ArrayBuffer + ): void => { + if (raw === true) { + resolve(new Uint8Array(result) as Return); + } else { + resolve( + idlDecode( + returnIdlType === undefined ? [] : [returnIdlType], + new Uint8Array(result) + )[0] as Return + ); + } + }; +} + +function createRejectCallback( + globalRejectId: string, + reject: (reason?: any) => void +): void { + globalThis._azleRejectCallbacks[globalRejectId] = ( + error: unknown + ): void => { + reject(error); + }; +} diff --git a/src/lib/stable/ic_apis/chunk.ts b/src/lib/stable/ic_apis/chunk.ts index cc4fbb8e3b..5ab5572c29 100644 --- a/src/lib/stable/ic_apis/chunk.ts +++ b/src/lib/stable/ic_apis/chunk.ts @@ -1,5 +1,4 @@ import { call } from './call'; -import { candidEncode } from './candid_encode'; import { canisterSelf } from './canister_self'; /** @@ -32,8 +31,8 @@ export async function chunk(): Promise { return undefined; } - await call(canisterSelf(), '_azle_chunk', { - raw: candidEncode('()'), - cycles: 0n + await call(canisterSelf(), '_azle_chunk', { + cycles: 0n, + raw: true }); } diff --git a/src/lib/stable/ic_apis/index.ts b/src/lib/stable/ic_apis/index.ts index feef17481c..dc5f21cc94 100644 --- a/src/lib/stable/ic_apis/index.ts +++ b/src/lib/stable/ic_apis/index.ts @@ -1,4 +1,4 @@ -export { call } from './call'; +export { call, CallError } from './call'; export { candidDecode } from './candid_decode'; export { candidEncode } from './candid_encode'; export { canisterCycleBalance } from './canister_cycle_balance'; @@ -20,7 +20,6 @@ export { msgReject } from './msg_reject'; export { msgRejectCode } from './msg_reject_code'; export { msgRejectMsg } from './msg_reject_msg'; export { msgReply } from './msg_reply'; -export { notify } from './notify'; export { performanceCounter } from './performance_counter'; export { setCertifiedData } from './set_certified_data'; export { setTimer } from './set_timer'; diff --git a/src/lib/stable/ic_apis/msg_reject_code.ts b/src/lib/stable/ic_apis/msg_reject_code.ts index e7054c4ad3..dd8736c48b 100644 --- a/src/lib/stable/ic_apis/msg_reject_code.ts +++ b/src/lib/stable/ic_apis/msg_reject_code.ts @@ -1,3 +1,5 @@ +export type RejectCode = 0 | 1 | 2 | 3 | 4 | 5 | 6; + /** * Returns the reject code from the most recently executed inter-canister call. * @@ -11,15 +13,15 @@ * - after a successful inter-canister await from a composite query * - after an unsuccessful inter-canister await from a composite query */ -export function msgRejectCode(): number { +export function msgRejectCode(): RejectCode { if ( globalThis._azleIcStable === undefined && globalThis._azleIcExperimental === undefined ) { - return 10_000; // 10_000 is an arbitrary number that is unlikely to ever be used by the IC + return 6; } return globalThis._azleIcExperimental !== undefined - ? Number(globalThis._azleIcExperimental.msgRejectCode()) + ? globalThis._azleIcExperimental.msgRejectCode() : globalThis._azleIcStable.msgRejectCode(); } diff --git a/src/lib/stable/ic_apis/notify.ts b/src/lib/stable/ic_apis/notify.ts deleted file mode 100644 index 6ae789cf87..0000000000 --- a/src/lib/stable/ic_apis/notify.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { IDL } from '@dfinity/candid'; -import { Principal } from '@dfinity/principal'; - -import { idlEncode } from '../execute_with_candid_serde'; - -/** - * Performs a cross-canister call without waiting for a response. - * - * @param canisterId - The target canister's ID as a Principal or string - * @param method - The method name to call on the target canister - * @param options - Optional parameters: - * @param options.paramIdlTypes - Array of IDL types for the parameters - * @param options.args - Array of arguments to pass to the method - * @param options.cycles - Amount of cycles to attach to the call (defaults to 0n). Represented as a u128 (max size 2^128 - 1) - * @param options.raw - Raw bytes to pass as arguments instead of Candid-encoded args - * - * @returns void - * - * @remarks - * - The call is "fire and forget" - errors are not returned - * - More efficient than await `call` when no response is needed - * - * - **Call Context**: - * - \@update - * - \@query(..., { composite: true }) - * - \@heartbeat - * - timer - * - after a successful inter-canister await - * - after an unsuccessful inter-canister await - * - after a successful inter-canister await from a composite query - * - after an unsuccessful inter-canister await from a composite query - */ -export function notify( - canisterId: Principal | string, - method: string, - options?: { - paramIdlTypes?: IDL.Type[]; - args?: any[]; - cycles?: bigint; - raw?: Uint8Array; - } -): void { - if ( - globalThis._azleIcStable === undefined && - globalThis._azleIcExperimental === undefined - ) { - return undefined; - } - - const paramIdlTypes = options?.paramIdlTypes ?? []; - const args = options?.args ?? []; - const cycles = options?.cycles ?? 0n; - const raw = options?.raw; - - const canisterIdPrincipal = - typeof canisterId === 'string' - ? Principal.fromText(canisterId) - : canisterId; - const canisterIdBytes = canisterIdPrincipal.toUint8Array(); - const argsRaw = raw === undefined ? idlEncode(paramIdlTypes, args) : raw; - const cyclesString = cycles.toString(); - - if (globalThis._azleIcExperimental !== undefined) { - return globalThis._azleIcExperimental.notifyRaw( - canisterIdBytes.buffer, - method, - argsRaw.buffer, - cyclesString - ); - } - - return globalThis._azleIcStable.notifyRaw( - canisterIdBytes, - method, - argsRaw, - cyclesString - ); -}