@layerzerolabs/protocol-stellar-v2 0.2.15 → 0.2.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +365 -297
- package/.turbo/turbo-lint.log +142 -110
- package/.turbo/turbo-test.log +1273 -1222
- package/Cargo.lock +20 -5
- package/Cargo.toml +4 -1
- package/contracts/ERROR_SPEC.md +44 -0
- package/contracts/common-macros/src/auth.rs +113 -0
- package/contracts/common-macros/src/contract_ttl.rs +84 -0
- package/contracts/common-macros/src/lib.rs +181 -30
- package/contracts/common-macros/src/lz_contract.rs +83 -0
- package/contracts/common-macros/src/tests/{ownable.rs → auth.rs} +48 -15
- package/contracts/common-macros/src/tests/contract_ttl.rs +662 -0
- package/contracts/common-macros/src/tests/mod.rs +2 -2
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_multisig_code.snap +20 -0
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_ownable_code.snap +24 -0
- package/contracts/common-macros/src/tests/snapshots/{common_macros__tests__ownable__snapshot_only_owner_preserves_function_signature.snap → common_macros__tests__auth__snapshot_only_auth_preserves_function_signature.snap} +4 -4
- package/contracts/common-macros/src/tests/snapshots/{common_macros__tests__contract_impl__snapshot_generated_contract_impl_code.snap → common_macros__tests__contract_ttl__snapshot_generated_contractimpl_code.snap} +3 -3
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__contract_ttl__snapshot_generated_contracttrait_code.snap +69 -0
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ttl_configurable__snapshot_generated_ttl_configurable_code.snap +7 -21
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +2 -2
- package/contracts/common-macros/src/ttl_configurable.rs +19 -34
- package/contracts/common-macros/src/ttl_extendable.rs +36 -0
- package/contracts/common-macros/src/upgradeable.rs +5 -5
- package/contracts/common-macros/src/utils.rs +9 -0
- package/contracts/endpoint-v2/src/constants.rs +4 -4
- package/contracts/endpoint-v2/src/endpoint_v2.rs +38 -40
- package/contracts/endpoint-v2/src/errors.rs +4 -3
- package/contracts/endpoint-v2/src/events.rs +1 -1
- package/contracts/endpoint-v2/src/message_lib_manager.rs +18 -5
- package/contracts/endpoint-v2/src/messaging_channel.rs +11 -1
- package/contracts/endpoint-v2/src/messaging_composer.rs +11 -1
- package/contracts/endpoint-v2/src/storage.rs +1 -1
- package/contracts/endpoint-v2/src/tests/endpoint_v2/pay_messaging_fees.rs +3 -3
- package/contracts/endpoint-v2/src/tests/endpoint_v2/quote.rs +1 -1
- package/contracts/endpoint-v2/src/tests/endpoint_v2/require_oapp_auth.rs +2 -2
- package/contracts/endpoint-v2/src/tests/endpoint_v2/send.rs +3 -3
- package/contracts/endpoint-v2/src/tests/endpoint_v2/set_zro.rs +4 -4
- package/contracts/endpoint-v2/src/tests/message_lib_manager/require_receive_lib_for_eid.rs +3 -3
- package/contracts/endpoint-v2/src/tests/message_lib_manager/require_registered.rs +1 -1
- package/contracts/endpoint-v2/src/tests/message_lib_manager/require_send_lib_for_eid.rs +3 -3
- package/contracts/endpoint-v2/src/tests/message_lib_manager/require_supported_eid.rs +1 -1
- package/contracts/endpoint-v2/src/tests/messaging_channel/clear_payload.rs +4 -4
- package/contracts/endpoint-v2/src/tests/messaging_channel/inbound.rs +1 -1
- package/contracts/layerzero-views/src/layerzero_view.rs +3 -6
- package/contracts/macro-integration-tests/tests/runtime/ownable/mod.rs +2 -2
- package/contracts/macro-integration-tests/tests/runtime/ownable/{only_owner_guard.rs → only_auth_guard.rs} +1 -1
- package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/configuration.rs +1 -1
- package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/freeze.rs +1 -1
- package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/mod.rs +0 -1
- package/contracts/macro-integration-tests/tests/ui/ownable/fail/{only_owner_missing_env.rs → only_auth_missing_env.rs} +3 -3
- package/contracts/macro-integration-tests/tests/ui/ownable/fail/{only_owner_missing_env.stderr → only_auth_missing_env.stderr} +4 -4
- package/contracts/macro-integration-tests/tests/ui/ownable/pass/namespacing_and_imports.rs +2 -3
- package/contracts/macro-integration-tests/tests/ui/ownable/pass/{only_owner_env_param_variants.rs → only_auth_env_param_variants.rs} +9 -9
- package/contracts/macro-integration-tests/tests/ui/ttl_configurable/pass/minimal_contract.rs +6 -6
- package/contracts/message-libs/message-lib-common/src/errors.rs +7 -2
- package/contracts/message-libs/message-lib-common/src/tests/packet_codec_v1/decode_packet_header.rs +3 -3
- package/contracts/message-libs/message-lib-common/src/tests/worker_options/append_lz_receive_option.rs +1 -2
- package/contracts/message-libs/message-lib-common/src/tests/worker_options/append_native_drop_option.rs +1 -2
- package/contracts/message-libs/message-lib-common/src/tests/worker_options/convert_legacy_options.rs +9 -9
- package/contracts/message-libs/message-lib-common/src/tests/worker_options/extract_type_3_options.rs +1 -1
- package/contracts/message-libs/message-lib-common/src/tests/worker_options/left_pad_to_bytes32.rs +1 -1
- package/contracts/message-libs/message-lib-common/src/tests/worker_options/split_worker_options.rs +2 -2
- package/contracts/message-libs/simple-message-lib/src/simple_message_lib.rs +7 -9
- package/contracts/message-libs/treasury/src/errors.rs +2 -2
- package/contracts/message-libs/treasury/src/events.rs +1 -1
- package/contracts/message-libs/treasury/src/interfaces/zro_fee_lib.rs +2 -2
- package/contracts/message-libs/treasury/src/storage.rs +1 -1
- package/contracts/message-libs/treasury/src/tests/treasury_tests.rs +1 -1
- package/contracts/message-libs/treasury/src/treasury.rs +14 -16
- package/contracts/message-libs/uln-302/src/receive_uln.rs +13 -2
- package/contracts/message-libs/uln-302/src/send_uln.rs +24 -4
- package/contracts/message-libs/uln-302/src/uln302.rs +6 -24
- package/contracts/oapps/counter/Cargo.toml +14 -1
- package/contracts/oapps/counter/integration_tests/mod.rs +4 -1
- package/contracts/oapps/counter/integration_tests/{setup.rs → setup_sml.rs} +48 -80
- package/contracts/oapps/counter/integration_tests/setup_uln.rs +997 -0
- package/contracts/oapps/counter/integration_tests/signing.rs +62 -0
- package/contracts/oapps/counter/integration_tests/test_with_sml.rs +24 -55
- package/contracts/oapps/counter/integration_tests/test_with_uln.rs +314 -0
- package/contracts/oapps/counter/integration_tests/utils.rs +196 -53
- package/contracts/oapps/counter/src/counter.rs +67 -43
- package/contracts/oapps/counter/src/tests/mod.rs +0 -13
- package/contracts/oapps/counter/src/tests/test_counter.rs +5 -7
- package/contracts/oapps/oapp/src/errors.rs +5 -1
- package/contracts/oapps/oapp/src/macro_tests/test_macros.rs +93 -78
- package/contracts/oapps/oapp/src/oapp_core.rs +36 -21
- package/contracts/oapps/oapp/src/oapp_options_type3.rs +48 -12
- package/contracts/oapps/oapp/src/oapp_receiver.rs +106 -41
- package/contracts/oapps/oapp/src/oapp_sender.rs +26 -34
- package/contracts/oapps/oapp/src/tests/test_oapp_core.rs +9 -8
- package/contracts/oapps/oapp/src/tests/test_oapp_options_type3.rs +25 -17
- package/contracts/oapps/oapp/src/tests/test_oapp_receiver.rs +7 -7
- package/contracts/oapps/oapp/src/tests/test_oapp_sender.rs +14 -15
- package/contracts/oapps/oapp-macros/src/generators.rs +128 -0
- package/contracts/oapps/oapp-macros/src/lib.rs +113 -56
- package/contracts/oapps/oft/Cargo.toml +10 -7
- package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_oft_fee.rs +3 -4
- package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_pausable.rs +2 -3
- package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_rate_limiter.rs +1 -1
- package/contracts/oapps/oft/integration-tests/mod.rs +1 -1
- package/contracts/oapps/oft/integration-tests/setup.rs +29 -110
- package/contracts/oapps/oft/integration-tests/utils.rs +254 -21
- package/contracts/oapps/oft/src/extensions/oft_fee.rs +13 -14
- package/contracts/oapps/oft/src/extensions/pausable.rs +4 -4
- package/contracts/oapps/oft/src/extensions/rate_limiter.rs +5 -5
- package/contracts/oapps/oft/src/lib.rs +11 -13
- package/contracts/oapps/oft/src/oft.rs +147 -225
- package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +9 -13
- package/contracts/oapps/oft/src/oft_types/mint_burn.rs +31 -14
- package/contracts/oapps/oft/src/oft_types/mod.rs +13 -0
- package/contracts/oapps/{oft-std → oft-core}/Cargo.toml +6 -4
- package/contracts/oapps/{oft-std → oft-core}/integration-tests/mod.rs +1 -1
- package/contracts/oapps/{oft-std → oft-core}/integration-tests/setup.rs +129 -30
- package/contracts/oapps/{oft → oft-core}/integration-tests/test_with_sml.rs +3 -3
- package/contracts/oapps/oft-core/integration-tests/utils.rs +201 -0
- package/contracts/oapps/oft-core/src/errors.rs +13 -0
- package/contracts/oapps/oft-core/src/lib.rs +18 -0
- package/contracts/oapps/oft-core/src/oft_core.rs +439 -0
- package/contracts/oapps/{oft → oft-core}/src/storage.rs +2 -0
- package/contracts/oapps/{oft → oft-core}/src/tests/mod.rs +0 -2
- package/contracts/oapps/{oft → oft-core}/src/tests/test_decimals.rs +2 -2
- package/contracts/oapps/{oft → oft-core}/src/tests/test_lz_receive.rs +7 -7
- package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_msg_codec.rs +4 -5
- package/contracts/oapps/{oft → oft-core}/src/tests/test_resolve_address.rs +3 -3
- package/contracts/oapps/{oft → oft-core}/src/tests/test_utils.rs +78 -37
- package/contracts/oapps/oft-core/src/types.rs +58 -0
- package/contracts/oapps/{oft → oft-core}/src/utils.rs +1 -1
- package/contracts/upgrader/src/lib.rs +4 -4
- package/contracts/utils/src/auth.rs +44 -0
- package/contracts/utils/src/errors.rs +31 -5
- package/contracts/utils/src/lib.rs +3 -0
- package/contracts/utils/src/multisig.rs +211 -0
- package/contracts/utils/src/ownable.rs +137 -13
- package/contracts/utils/src/tests/buffer_reader.rs +6 -6
- package/contracts/utils/src/tests/buffer_writer.rs +6 -6
- package/contracts/utils/src/tests/bytes_ext.rs +2 -4
- package/contracts/utils/src/tests/mod.rs +1 -0
- package/contracts/utils/src/tests/multisig.rs +731 -0
- package/contracts/utils/src/tests/option_ext.rs +2 -5
- package/contracts/utils/src/tests/ownable.rs +456 -7
- package/contracts/utils/src/tests/ttl_configurable.rs +27 -16
- package/contracts/utils/src/tests/upgradeable.rs +4 -2
- package/contracts/utils/src/ttl_configurable.rs +23 -8
- package/contracts/utils/src/ttl_extendable.rs +27 -0
- package/contracts/utils/src/upgradeable.rs +2 -0
- package/contracts/workers/dvn/Cargo.toml +1 -1
- package/contracts/workers/dvn/src/auth.rs +7 -7
- package/contracts/workers/dvn/src/dvn.rs +10 -38
- package/contracts/workers/dvn/src/errors.rs +0 -7
- package/contracts/workers/dvn/src/events.rs +1 -14
- package/contracts/workers/dvn/src/interfaces/dvn.rs +2 -2
- package/contracts/workers/dvn/src/interfaces/mod.rs +0 -2
- package/contracts/workers/dvn/src/storage.rs +3 -13
- package/contracts/workers/dvn/src/tests/auth.rs +4 -4
- package/contracts/workers/dvn/src/tests/dvn.rs +1 -2
- package/contracts/workers/dvn/src/tests/multisig/set_signer.rs +7 -8
- package/contracts/workers/dvn/src/tests/multisig/set_threshold.rs +11 -8
- package/contracts/workers/dvn/src/tests/multisig/verify_signatures.rs +11 -12
- package/contracts/workers/dvn/src/tests/setup.rs +5 -5
- package/contracts/workers/dvn-fee-lib/Cargo.toml +1 -1
- package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +3 -6
- package/contracts/workers/executor/src/auth.rs +80 -16
- package/contracts/workers/executor/src/executor.rs +5 -31
- package/contracts/workers/executor/src/storage.rs +2 -9
- package/contracts/workers/executor-fee-lib/Cargo.toml +1 -1
- package/contracts/workers/executor-fee-lib/src/executor_fee_lib.rs +3 -6
- package/contracts/workers/executor-helper/Cargo.toml +1 -1
- package/contracts/workers/executor-helper/src/executor_helper.rs +53 -73
- package/contracts/workers/price-feed/Cargo.toml +1 -1
- package/contracts/workers/price-feed/src/price_feed.rs +7 -10
- package/contracts/workers/worker/src/errors.rs +4 -0
- package/contracts/workers/worker/src/tests/worker.rs +7 -6
- package/contracts/workers/worker/src/worker.rs +20 -16
- package/package.json +7 -5
- package/sdk/.turbo/turbo-build.log +1 -0
- package/sdk/.turbo/turbo-test.log +1019 -0
- package/sdk/dist/generated/bml.d.ts +95 -8
- package/sdk/dist/generated/bml.js +95 -36
- package/sdk/dist/generated/counter.d.ts +289 -44
- package/sdk/dist/generated/counter.js +119 -49
- package/sdk/dist/generated/dvn.d.ts +312 -229
- package/sdk/dist/generated/dvn.js +144 -83
- package/sdk/dist/generated/dvn_fee_lib.d.ts +258 -63
- package/sdk/dist/generated/dvn_fee_lib.js +95 -26
- package/sdk/dist/generated/endpoint.d.ts +219 -24
- package/sdk/dist/generated/endpoint.js +108 -41
- package/sdk/dist/generated/executor.d.ts +239 -87
- package/sdk/dist/generated/executor.js +135 -63
- package/sdk/dist/generated/executor_fee_lib.d.ts +278 -74
- package/sdk/dist/generated/executor_fee_lib.js +135 -59
- package/sdk/dist/generated/executor_helper.d.ts +163 -21
- package/sdk/dist/generated/executor_helper.js +124 -52
- package/sdk/dist/generated/oft.d.ts +1842 -0
- package/sdk/dist/generated/oft.js +345 -0
- package/sdk/dist/generated/price_feed.d.ts +258 -63
- package/sdk/dist/generated/price_feed.js +95 -26
- package/sdk/dist/generated/sml.d.ts +235 -34
- package/sdk/dist/generated/sml.js +126 -53
- package/sdk/dist/generated/treasury.d.ts +1016 -0
- package/sdk/dist/generated/treasury.js +248 -0
- package/sdk/dist/generated/uln302.d.ts +235 -34
- package/sdk/dist/generated/uln302.js +126 -53
- package/sdk/dist/generated/upgrader.d.ts +17 -2
- package/sdk/dist/generated/upgrader.js +19 -1
- package/sdk/dist/index.d.ts +2 -1
- package/sdk/dist/index.js +2 -1
- package/sdk/package.json +6 -3
- package/sdk/src/index.ts +2 -1
- package/sdk/test/counter-sml.test.ts +376 -0
- package/sdk/test/counter-uln.test.ts +493 -0
- package/sdk/test/{oft.test.ts → oft-sml.test.ts} +196 -321
- package/sdk/test/suites/constants.ts +22 -2
- package/sdk/test/suites/globalSetup.ts +450 -0
- package/sdk/test/suites/localnet.ts +23 -6
- package/sdk/test/upgrader.test.ts +7 -16
- package/sdk/test/utils.ts +558 -85
- package/sdk/turbo.json +8 -0
- package/sdk/vitest.config.ts +21 -0
- package/tools/ts-bindings-gen/Cargo.toml +2 -0
- package/tools/ts-bindings-gen/src/main.rs +52 -4
- package/contracts/common-macros/src/contract_impl.rs +0 -52
- package/contracts/common-macros/src/ownable.rs +0 -41
- package/contracts/common-macros/src/tests/contract_impl.rs +0 -386
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ownable__snapshot_generated_ownable_code.snap +0 -12
- package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/extend_instance_ttl.rs +0 -50
- package/contracts/oapps/oapp-macros/src/oapp_core.rs +0 -41
- package/contracts/oapps/oapp-macros/src/oapp_full.rs +0 -21
- package/contracts/oapps/oapp-macros/src/oapp_options_type3.rs +0 -31
- package/contracts/oapps/oapp-macros/src/oapp_receiver.rs +0 -48
- package/contracts/oapps/oapp-macros/src/oapp_sender.rs +0 -21
- package/contracts/oapps/oapp-macros/src/util.rs +0 -107
- package/contracts/oapps/oft/src/constants.rs +0 -5
- package/contracts/oapps/oft/src/default_oft_impl.rs +0 -152
- package/contracts/oapps/oft/src/errors.rs +0 -8
- package/contracts/oapps/oft/src/interfaces/mint_burn_token.rs +0 -23
- package/contracts/oapps/oft/src/interfaces/mod.rs +0 -3
- package/contracts/oapps/oft/src/tests/extensions/mod.rs +0 -11
- package/contracts/oapps/oft/src/tests/extensions/setup.rs +0 -903
- package/contracts/oapps/oft/src/tests/extensions/test_oft_fee.rs +0 -749
- package/contracts/oapps/oft/src/tests/extensions/test_pausable.rs +0 -432
- package/contracts/oapps/oft/src/tests/extensions/test_rate_limiter.rs +0 -1078
- package/contracts/oapps/oft/src/types.rs +0 -38
- package/contracts/oapps/oft-std/integration-tests/utils.rs +0 -427
- package/contracts/oapps/oft-std/src/lib.rs +0 -16
- package/contracts/oapps/oft-std/src/oft.rs +0 -156
- package/contracts/workers/dvn/src/interfaces/multisig.rs +0 -56
- package/contracts/workers/dvn/src/multisig.rs +0 -157
- package/sdk/dist/generated/oft_std.d.ts +0 -1544
- package/sdk/dist/generated/oft_std.js +0 -271
- package/sdk/test/index.test.ts +0 -375
- /package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/mod.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/codec/mod.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/codec/oft_compose_msg_codec.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/codec/oft_msg_codec.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/events.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_compose_msg_codec.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_version.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/tests/test_quote_oft.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/tests/test_quote_send.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/tests/test_send.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/tests/test_token.rs +0 -0
- /package/sdk/test/suites/{testUpgradeable.ts → dummyContractClient.ts} +0 -0
package/sdk/test/utils.ts
CHANGED
|
@@ -1,26 +1,204 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { keccak_256 } from '@noble/hashes/sha3';
|
|
2
|
+
import * as secp from '@noble/secp256k1';
|
|
3
|
+
import {
|
|
4
|
+
Account,
|
|
5
|
+
Address,
|
|
6
|
+
authorizeEntry,
|
|
7
|
+
BASE_FEE,
|
|
8
|
+
Contract,
|
|
9
|
+
contract,
|
|
10
|
+
hash,
|
|
11
|
+
Keypair,
|
|
12
|
+
nativeToScVal,
|
|
13
|
+
scValToNative,
|
|
14
|
+
TransactionBuilder,
|
|
15
|
+
xdr,
|
|
16
|
+
} from '@stellar/stellar-sdk';
|
|
17
|
+
import * as rpc from '@stellar/stellar-sdk/rpc';
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
DEFAULT_DEPLOYER,
|
|
21
|
+
NATIVE_TOKEN_ADDRESS,
|
|
22
|
+
NETWORK_PASSPHRASE,
|
|
23
|
+
RPC_URL,
|
|
24
|
+
} from './suites/constants';
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Client Factory Helper
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Helper to create a Soroban contract client with DEFAULT_DEPLOYER as signer.
|
|
32
|
+
* This is used to create clients for protocol contracts from their addresses.
|
|
33
|
+
*/
|
|
34
|
+
export function createClient<T>(
|
|
35
|
+
ClientClass: new (options: {
|
|
36
|
+
contractId: string;
|
|
37
|
+
publicKey: string;
|
|
38
|
+
signTransaction: (tx: string) => Promise<{ signedTxXdr: string; signerAddress: string }>;
|
|
39
|
+
rpcUrl: string;
|
|
40
|
+
networkPassphrase: string;
|
|
41
|
+
allowHttp: boolean;
|
|
42
|
+
}) => T,
|
|
43
|
+
contractId: string,
|
|
44
|
+
): T {
|
|
45
|
+
return new ClientClass({
|
|
46
|
+
contractId,
|
|
47
|
+
publicKey: DEFAULT_DEPLOYER.publicKey(),
|
|
48
|
+
signTransaction: async (tx: string) => {
|
|
49
|
+
const transaction = TransactionBuilder.fromXDR(tx, NETWORK_PASSPHRASE);
|
|
50
|
+
transaction.sign(DEFAULT_DEPLOYER);
|
|
51
|
+
return {
|
|
52
|
+
signedTxXdr: transaction.toXDR(),
|
|
53
|
+
signerAddress: DEFAULT_DEPLOYER.publicKey(),
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
rpcUrl: RPC_URL,
|
|
57
|
+
networkPassphrase: NETWORK_PASSPHRASE,
|
|
58
|
+
allowHttp: true,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ============================================================================
|
|
63
|
+
// Token Balance Helpers
|
|
64
|
+
// ============================================================================
|
|
3
65
|
|
|
4
66
|
/**
|
|
5
|
-
*
|
|
67
|
+
* Helper to get token balance for an address using SAC (Stellar Asset Contract).
|
|
68
|
+
* Works for any token including native XLM.
|
|
6
69
|
*
|
|
7
|
-
* The
|
|
8
|
-
*
|
|
9
|
-
|
|
10
|
-
|
|
70
|
+
* @param tokenAddress - The SAC contract address (defaults to native XLM)
|
|
71
|
+
* @param accountAddress - The account to check balance for
|
|
72
|
+
*/
|
|
73
|
+
export async function getTokenBalance(
|
|
74
|
+
accountAddress: string,
|
|
75
|
+
tokenAddress: string = NATIVE_TOKEN_ADDRESS,
|
|
76
|
+
): Promise<bigint> {
|
|
77
|
+
const server = new rpc.Server(RPC_URL, { allowHttp: true });
|
|
78
|
+
const tokenContract = new Contract(tokenAddress);
|
|
79
|
+
|
|
80
|
+
// Build the balance call
|
|
81
|
+
const balanceOp = tokenContract.call(
|
|
82
|
+
'balance',
|
|
83
|
+
nativeToScVal(Address.fromString(accountAddress), { type: 'address' }),
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const account = await server.getAccount(DEFAULT_DEPLOYER.publicKey());
|
|
87
|
+
const tx = new TransactionBuilder(account, {
|
|
88
|
+
fee: BASE_FEE,
|
|
89
|
+
networkPassphrase: NETWORK_PASSPHRASE,
|
|
90
|
+
})
|
|
91
|
+
.addOperation(balanceOp)
|
|
92
|
+
.setTimeout(30)
|
|
93
|
+
.build();
|
|
94
|
+
|
|
95
|
+
const simulated = await server.simulateTransaction(tx);
|
|
96
|
+
if (rpc.Api.isSimulationError(simulated)) {
|
|
97
|
+
throw new Error(`Balance query failed: ${JSON.stringify(simulated)}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Extract result from simulation
|
|
101
|
+
const result = (simulated as rpc.Api.SimulateTransactionSuccessResponse).result;
|
|
102
|
+
if (result?.retval) {
|
|
103
|
+
return scValToNative(result.retval) as bigint;
|
|
104
|
+
}
|
|
105
|
+
return 0n;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Helper to get native token (XLM) balance for an address.
|
|
110
|
+
* Convenience wrapper around getTokenBalance.
|
|
111
|
+
*/
|
|
112
|
+
export async function getNativeBalance(accountAddress: string): Promise<bigint> {
|
|
113
|
+
return getTokenBalance(accountAddress, NATIVE_TOKEN_ADDRESS);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ============================================================================
|
|
117
|
+
// Secp256k1 Key Pair for DVN Multisig
|
|
118
|
+
// ============================================================================
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* A secp256k1 key pair with private key and derived Ethereum-style address.
|
|
122
|
+
* Used for DVN multisig signing.
|
|
123
|
+
*/
|
|
124
|
+
export class Secp256k1KeyPair {
|
|
125
|
+
private privateKey: Uint8Array;
|
|
126
|
+
public readonly ethAddress: Buffer;
|
|
127
|
+
|
|
128
|
+
constructor(privateKey: Uint8Array | Buffer | string) {
|
|
129
|
+
if (typeof privateKey === 'string') {
|
|
130
|
+
// Remove 0x prefix if present
|
|
131
|
+
const hex = privateKey.startsWith('0x') ? privateKey.slice(2) : privateKey;
|
|
132
|
+
this.privateKey = Buffer.from(hex, 'hex');
|
|
133
|
+
} else {
|
|
134
|
+
this.privateKey = new Uint8Array(privateKey);
|
|
135
|
+
}
|
|
136
|
+
this.ethAddress = this.deriveEthAddress();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Generate a random key pair.
|
|
141
|
+
*/
|
|
142
|
+
static generate(): Secp256k1KeyPair {
|
|
143
|
+
const privateKey = secp.utils.randomPrivateKey();
|
|
144
|
+
return new Secp256k1KeyPair(privateKey);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Derive Ethereum-style address from the public key.
|
|
149
|
+
* Address = last 20 bytes of keccak256(uncompressed_pubkey[1:65])
|
|
150
|
+
*/
|
|
151
|
+
private deriveEthAddress(): Buffer {
|
|
152
|
+
const publicKey = secp.getPublicKey(this.privateKey, false); // uncompressed
|
|
153
|
+
const pubkeyWithoutPrefix = publicKey.slice(1); // remove 0x04 prefix
|
|
154
|
+
const hash = keccak_256(pubkeyWithoutPrefix);
|
|
155
|
+
return Buffer.from(hash.slice(12)); // last 20 bytes
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Sign a 32-byte digest and return a 65-byte signature (r || s || v).
|
|
160
|
+
*/
|
|
161
|
+
async sign(digest: Uint8Array): Promise<Buffer> {
|
|
162
|
+
const [signature, recoveryId] = await secp.sign(digest, this.privateKey, {
|
|
163
|
+
canonical: true,
|
|
164
|
+
recovered: true,
|
|
165
|
+
der: false,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const v = 27 + recoveryId;
|
|
169
|
+
const result = Buffer.alloc(65);
|
|
170
|
+
result.set(signature, 0); // r (32 bytes) + s (32 bytes)
|
|
171
|
+
result[64] = v;
|
|
172
|
+
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ============================================================================
|
|
178
|
+
// DVN Abstract Account Auth Signing
|
|
179
|
+
// ============================================================================
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Signs the DVN abstract account's auth entries.
|
|
11
183
|
*
|
|
12
|
-
*
|
|
184
|
+
* The DVN contract implements CustomAccountInterface with Signature = TransactionAuthData.
|
|
185
|
+
* TransactionAuthData contains:
|
|
186
|
+
* - vid: u32 - Verifier ID
|
|
187
|
+
* - expiration: u64 - Ledger timestamp for when auth expires
|
|
188
|
+
* - signatures: Vec<BytesN<65>> - Secp256k1 signatures from multisig signers
|
|
189
|
+
* - sender: Sender - Either None or Admin(public_key, ed25519_signature)
|
|
13
190
|
*/
|
|
14
|
-
export async function
|
|
15
|
-
|
|
191
|
+
export async function signDvnAuthEntries<T>(
|
|
192
|
+
dvnAddress: string,
|
|
193
|
+
vid: number,
|
|
16
194
|
adminKeypair: Keypair,
|
|
195
|
+
multisigSigners: Secp256k1KeyPair[],
|
|
17
196
|
assembledTx: contract.AssembledTransaction<T>,
|
|
18
197
|
networkPassphrase: string,
|
|
19
198
|
): Promise<void> {
|
|
20
|
-
const
|
|
21
|
-
const adminAddr = Address.fromString(adminKeypair.publicKey());
|
|
199
|
+
const dvnAddr = Address.fromString(dvnAddress);
|
|
22
200
|
|
|
23
|
-
console.log('\n🔄 Simulating transaction to get the auth entries');
|
|
201
|
+
console.log('\n🔄 Simulating DVN transaction to get the auth entries');
|
|
24
202
|
await assembledTx.simulate();
|
|
25
203
|
|
|
26
204
|
// Print debug info
|
|
@@ -29,7 +207,7 @@ export async function signExecutorAuthEntries<T>(
|
|
|
29
207
|
|
|
30
208
|
const networkId = hash(Buffer.from(networkPassphrase));
|
|
31
209
|
|
|
32
|
-
//
|
|
210
|
+
// Custom authorizer for DVN abstract account
|
|
33
211
|
const customAuthorizeEntry = async (
|
|
34
212
|
entry: xdr.SorobanAuthorizationEntry,
|
|
35
213
|
_signer: Keypair | ((preimage: xdr.HashIdPreimage) => Promise<unknown>),
|
|
@@ -45,15 +223,15 @@ export async function signExecutorAuthEntries<T>(
|
|
|
45
223
|
const credentialAddress = Address.fromScAddress(addressCred.address());
|
|
46
224
|
const rootInvocation = entry.rootInvocation();
|
|
47
225
|
|
|
48
|
-
if (credentialAddress.toString() !==
|
|
226
|
+
if (credentialAddress.toString() !== dvnAddr.toString()) {
|
|
49
227
|
throw new Error('Credential address mismatch');
|
|
50
228
|
}
|
|
51
229
|
|
|
52
|
-
// Log the
|
|
53
|
-
console.log('\n🌳
|
|
230
|
+
// Log the DVN's auth entry tree
|
|
231
|
+
console.log('\n🌳 DVN Auth Entry Tree:');
|
|
54
232
|
logInvocationTree(rootInvocation, 0);
|
|
55
233
|
|
|
56
|
-
// Compute the signature_payload hash
|
|
234
|
+
// 1. Compute the signature_payload (soroban authorization hash)
|
|
57
235
|
const preimage = xdr.HashIdPreimage.envelopeTypeSorobanAuthorization(
|
|
58
236
|
new xdr.HashIdPreimageSorobanAuthorization({
|
|
59
237
|
networkId,
|
|
@@ -63,34 +241,79 @@ export async function signExecutorAuthEntries<T>(
|
|
|
63
241
|
}),
|
|
64
242
|
);
|
|
65
243
|
const signaturePayload = hash(preimage.toXDR());
|
|
66
|
-
console.log(
|
|
244
|
+
console.log(
|
|
245
|
+
'\n📝 Signature payload (soroban auth hash):',
|
|
246
|
+
signaturePayload.toString('hex'),
|
|
247
|
+
);
|
|
67
248
|
|
|
68
|
-
// Sign the signature_payload with admin's Ed25519 key
|
|
249
|
+
// 2. Sign the signature_payload with admin's Ed25519 key
|
|
69
250
|
const adminSignature = adminKeypair.sign(signaturePayload);
|
|
70
251
|
console.log(
|
|
71
|
-
'✍️ Admin signature created:',
|
|
252
|
+
'✍️ Admin Ed25519 signature created:',
|
|
72
253
|
adminSignature.toString('hex').slice(0, 32) + '...',
|
|
73
254
|
);
|
|
74
255
|
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
|
|
256
|
+
// 3. Extract calls from the auth entry and compute the multisig hash
|
|
257
|
+
// The expiration is the ledger timestamp when the auth expires
|
|
258
|
+
// We use validUntilLedgerSeq * 5 as a rough approximation (5 seconds per ledger)
|
|
259
|
+
const expiration = BigInt(validUntilLedgerSeq) * 5n + BigInt(Math.floor(Date.now() / 1000));
|
|
260
|
+
const calls = collectCallsFromInvocation(rootInvocation);
|
|
261
|
+
const callsXdr = serializeCallsToXdr(calls);
|
|
262
|
+
const callHash = computeCallHash(vid, expiration, callsXdr);
|
|
263
|
+
console.log('📝 Call hash for multisig:', Buffer.from(callHash).toString('hex'));
|
|
264
|
+
|
|
265
|
+
// 4. Sign the call hash with multisig signers
|
|
266
|
+
const signatures: Buffer[] = [];
|
|
267
|
+
for (const signer of multisigSigners) {
|
|
268
|
+
const sig = await signer.sign(callHash);
|
|
269
|
+
signatures.push(sig);
|
|
270
|
+
console.log('✍️ Multisig signature created:', sig.toString('hex').slice(0, 32) + '...');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// 5. Sort signatures by recovered signer address (ascending)
|
|
274
|
+
// This is required by the DVN contract's verify_signatures function
|
|
275
|
+
const signaturesWithAddresses = await Promise.all(
|
|
276
|
+
signatures.map(async (sig, i) => ({
|
|
277
|
+
sig,
|
|
278
|
+
address: multisigSigners[i].ethAddress,
|
|
279
|
+
})),
|
|
280
|
+
);
|
|
281
|
+
signaturesWithAddresses.sort((a, b) => a.address.compare(b.address));
|
|
282
|
+
const sortedSignatures = signaturesWithAddresses.map((s) => s.sig);
|
|
283
|
+
|
|
284
|
+
// 6. Build TransactionAuthData as ScVal
|
|
285
|
+
// struct TransactionAuthData { vid: u32, expiration: u64, signatures: Vec<BytesN<65>>, sender: Sender }
|
|
286
|
+
// enum Sender { None, Admin(BytesN<32>, BytesN<64>) }
|
|
287
|
+
const transactionAuthDataScVal = xdr.ScVal.scvMap([
|
|
78
288
|
new xdr.ScMapEntry({
|
|
79
|
-
key: xdr.ScVal.scvSymbol('
|
|
80
|
-
val: xdr.ScVal.
|
|
289
|
+
key: xdr.ScVal.scvSymbol('expiration'),
|
|
290
|
+
val: xdr.ScVal.scvU64(new xdr.Uint64(expiration)),
|
|
81
291
|
}),
|
|
82
292
|
new xdr.ScMapEntry({
|
|
83
|
-
key: xdr.ScVal.scvSymbol('
|
|
84
|
-
|
|
293
|
+
key: xdr.ScVal.scvSymbol('sender'),
|
|
294
|
+
// Sender::Admin(public_key, signature)
|
|
295
|
+
val: xdr.ScVal.scvVec([
|
|
296
|
+
xdr.ScVal.scvSymbol('Admin'),
|
|
297
|
+
xdr.ScVal.scvBytes(adminKeypair.rawPublicKey()),
|
|
298
|
+
xdr.ScVal.scvBytes(adminSignature),
|
|
299
|
+
]),
|
|
300
|
+
}),
|
|
301
|
+
new xdr.ScMapEntry({
|
|
302
|
+
key: xdr.ScVal.scvSymbol('signatures'),
|
|
303
|
+
val: xdr.ScVal.scvVec(sortedSignatures.map((sig) => xdr.ScVal.scvBytes(sig))),
|
|
304
|
+
}),
|
|
305
|
+
new xdr.ScMapEntry({
|
|
306
|
+
key: xdr.ScVal.scvSymbol('vid'),
|
|
307
|
+
val: xdr.ScVal.scvU32(vid),
|
|
85
308
|
}),
|
|
86
309
|
]);
|
|
87
310
|
|
|
88
|
-
// Return
|
|
311
|
+
// Return DVN's auth entry with TransactionAuthData
|
|
89
312
|
const newCred = new xdr.SorobanAddressCredentials({
|
|
90
313
|
address: addressCred.address(),
|
|
91
314
|
nonce: addressCred.nonce(),
|
|
92
315
|
signatureExpirationLedger: validUntilLedgerSeq,
|
|
93
|
-
signature:
|
|
316
|
+
signature: transactionAuthDataScVal,
|
|
94
317
|
});
|
|
95
318
|
|
|
96
319
|
return new xdr.SorobanAuthorizationEntry({
|
|
@@ -99,61 +322,316 @@ export async function signExecutorAuthEntries<T>(
|
|
|
99
322
|
});
|
|
100
323
|
};
|
|
101
324
|
|
|
102
|
-
// Check if the
|
|
103
|
-
if (remaining.includes(
|
|
104
|
-
// Sign the executor's auth entry
|
|
105
|
-
// Note: signAuthEntries internally calls simulate() after signing
|
|
325
|
+
// Check if the DVN needs to sign
|
|
326
|
+
if (remaining.includes(dvnAddr.toString())) {
|
|
106
327
|
await assembledTx.signAuthEntries({
|
|
107
|
-
address:
|
|
328
|
+
address: dvnAddr.toString(),
|
|
108
329
|
authorizeEntry: customAuthorizeEntry,
|
|
109
330
|
});
|
|
110
331
|
|
|
111
|
-
|
|
112
|
-
console.log('\n🔄 Executor auth signed');
|
|
332
|
+
console.log('\n🔄 DVN auth signed');
|
|
113
333
|
|
|
114
|
-
// Check remaining signers after executor auth
|
|
115
334
|
remaining = assembledTx.needsNonInvokerSigningBy({
|
|
116
335
|
includeAlreadySigned: false,
|
|
117
336
|
});
|
|
118
|
-
console.log('📋 Remaining signers after
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (remaining.includes(adminAddr.toString())) {
|
|
122
|
-
console.log('\n✍️ Admin signing auth entries...');
|
|
123
|
-
|
|
124
|
-
// Log the admin's auth entry tree before signing
|
|
125
|
-
assembledTx.built?.operations
|
|
126
|
-
.flatMap((op) => ('auth' in op ? (op.auth as xdr.SorobanAuthorizationEntry[]) : []))
|
|
127
|
-
.filter((entry) => {
|
|
128
|
-
const cred = entry.credentials();
|
|
129
|
-
return (
|
|
130
|
-
cred.switch() === xdr.SorobanCredentialsType.sorobanCredentialsAddress() &&
|
|
131
|
-
Address.fromScAddress(cred.address().address()).toString() ===
|
|
132
|
-
adminAddr.toString()
|
|
133
|
-
);
|
|
134
|
-
})
|
|
135
|
-
.forEach((entry) => {
|
|
136
|
-
console.log('\n🌳 Admin Auth Entry Tree:');
|
|
137
|
-
logInvocationTree(entry.rootInvocation(), 0);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
// Use SDK's basicNodeSigner for account signing
|
|
141
|
-
const adminSigner = basicNodeSigner(adminKeypair, networkPassphrase);
|
|
142
|
-
console.log('✅ Using SDK basicNodeSigner for admin account signing');
|
|
143
|
-
|
|
144
|
-
// Sign admin's auth entries using signAuthEntries with SDK's signAuthEntry
|
|
145
|
-
await assembledTx.signAuthEntries({
|
|
146
|
-
address: adminAddr.toString(),
|
|
147
|
-
signAuthEntry: adminSigner.signAuthEntry,
|
|
148
|
-
});
|
|
337
|
+
console.log('📋 Remaining signers after DVN:', remaining);
|
|
149
338
|
}
|
|
150
339
|
|
|
151
|
-
// Final re-simulation
|
|
152
|
-
|
|
153
|
-
// adds CPU costs that may not be fully captured in earlier simulations.
|
|
154
|
-
console.log('\n🔄 Final re-simulation with all auth entries signed to get the fee');
|
|
340
|
+
// Final re-simulation
|
|
341
|
+
console.log('\n🔄 Final re-simulation with DVN auth entries signed');
|
|
155
342
|
await assembledTx.simulate();
|
|
156
|
-
console.log('✅ Final simulation complete
|
|
343
|
+
console.log('✅ Final simulation complete');
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Represents a contract call for multisig authorization.
|
|
348
|
+
*/
|
|
349
|
+
interface Call {
|
|
350
|
+
to: string; // Contract address
|
|
351
|
+
func: string; // Function name
|
|
352
|
+
args: xdr.ScVal[]; // Function arguments
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Collects all contract calls from an invocation tree.
|
|
357
|
+
*/
|
|
358
|
+
function collectCallsFromInvocation(invocation: xdr.SorobanAuthorizedInvocation): Call[] {
|
|
359
|
+
const calls: Call[] = [];
|
|
360
|
+
collectCallsRecursive(invocation, calls);
|
|
361
|
+
return calls;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function collectCallsRecursive(invocation: xdr.SorobanAuthorizedInvocation, calls: Call[]): void {
|
|
365
|
+
const fn = invocation.function();
|
|
366
|
+
|
|
367
|
+
if (
|
|
368
|
+
fn.switch() === xdr.SorobanAuthorizedFunctionType.sorobanAuthorizedFunctionTypeContractFn()
|
|
369
|
+
) {
|
|
370
|
+
const contractFn = fn.contractFn();
|
|
371
|
+
const contractAddr = Address.fromScAddress(contractFn.contractAddress());
|
|
372
|
+
|
|
373
|
+
calls.push({
|
|
374
|
+
to: contractAddr.toString(),
|
|
375
|
+
func: contractFn.functionName().toString(),
|
|
376
|
+
args: contractFn.args(),
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Process sub-invocations
|
|
381
|
+
for (const sub of invocation.subInvocations()) {
|
|
382
|
+
collectCallsRecursive(sub, calls);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Serializes calls to XDR format matching Soroban's Vec<Call> serialization.
|
|
388
|
+
*
|
|
389
|
+
* Call struct: { args: Vec<Val>, func: Symbol, to: Address }
|
|
390
|
+
* Serialized as ScMap with keys in alphabetical order.
|
|
391
|
+
*/
|
|
392
|
+
function serializeCallsToXdr(calls: Call[]): Buffer {
|
|
393
|
+
const callScVals = calls.map((call) =>
|
|
394
|
+
xdr.ScVal.scvMap([
|
|
395
|
+
new xdr.ScMapEntry({
|
|
396
|
+
key: xdr.ScVal.scvSymbol('args'),
|
|
397
|
+
val: xdr.ScVal.scvVec(call.args),
|
|
398
|
+
}),
|
|
399
|
+
new xdr.ScMapEntry({
|
|
400
|
+
key: xdr.ScVal.scvSymbol('func'),
|
|
401
|
+
val: xdr.ScVal.scvSymbol(call.func),
|
|
402
|
+
}),
|
|
403
|
+
new xdr.ScMapEntry({
|
|
404
|
+
key: xdr.ScVal.scvSymbol('to'),
|
|
405
|
+
val: Address.fromString(call.to).toScVal(),
|
|
406
|
+
}),
|
|
407
|
+
]),
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
const vecScVal = xdr.ScVal.scvVec(callScVals);
|
|
411
|
+
return Buffer.from(vecScVal.toXDR());
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Computes the call hash for multisig signing.
|
|
416
|
+
* hash = keccak256(vid.to_be_bytes(4) || expiration.to_be_bytes(8) || calls_xdr)
|
|
417
|
+
*/
|
|
418
|
+
function computeCallHash(vid: number, expiration: bigint, callsXdr: Buffer): Uint8Array {
|
|
419
|
+
// vid as 4-byte big-endian
|
|
420
|
+
const vidBytes = Buffer.alloc(4);
|
|
421
|
+
vidBytes.writeUInt32BE(vid, 0);
|
|
422
|
+
|
|
423
|
+
// expiration as 8-byte big-endian
|
|
424
|
+
const expirationBytes = Buffer.alloc(8);
|
|
425
|
+
expirationBytes.writeBigUInt64BE(expiration, 0);
|
|
426
|
+
|
|
427
|
+
// Concatenate and hash
|
|
428
|
+
const data = Buffer.concat([vidBytes, expirationBytes, callsXdr]);
|
|
429
|
+
return keccak_256(data);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// ============================================================================
|
|
433
|
+
// Executor Abstract Account Auth Signing (with Non-Root Auth Support)
|
|
434
|
+
// ============================================================================
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Signs and sends a transaction with Executor abstract account auth entries.
|
|
438
|
+
*
|
|
439
|
+
* The Executor contract implements CustomAccountInterface with Signature = ExecutorSignature.
|
|
440
|
+
* ExecutorSignature contains:
|
|
441
|
+
* - public_key: BytesN<32> - Admin's Ed25519 public key
|
|
442
|
+
* - signature: BytesN<64> - Ed25519 signature over the signature_payload
|
|
443
|
+
*
|
|
444
|
+
* This function uses `record_allow_nonroot` simulation mode to capture non-root auth entries,
|
|
445
|
+
* which is required because the executor authorizes `lz_receive`/`lz_compose` calls that are
|
|
446
|
+
* invoked by the ExecutorHelper contract (not the root invoker).
|
|
447
|
+
*
|
|
448
|
+
* @returns The transaction result after sending
|
|
449
|
+
*/
|
|
450
|
+
export async function signAndSendWithExecutorAuth<T>(
|
|
451
|
+
executorAddress: string,
|
|
452
|
+
adminKeypair: Keypair,
|
|
453
|
+
assembledTx: contract.AssembledTransaction<T>,
|
|
454
|
+
networkPassphrase: string,
|
|
455
|
+
): Promise<rpc.Api.GetSuccessfulTransactionResponse> {
|
|
456
|
+
const executorAddr = Address.fromString(executorAddress);
|
|
457
|
+
const server = new rpc.Server(RPC_URL, { allowHttp: true });
|
|
458
|
+
|
|
459
|
+
console.log('\n🔄 Building transaction for non-root auth...');
|
|
460
|
+
|
|
461
|
+
// 1. Build the raw transaction (don't simulate yet)
|
|
462
|
+
const rawTx = assembledTx.raw!.build();
|
|
463
|
+
|
|
464
|
+
// 2. Simulate with record_allow_nonroot to capture non-root auth entries
|
|
465
|
+
// This is required because the executor's auth is not the root invocation
|
|
466
|
+
console.log('🔄 Simulating with record_allow_nonroot...');
|
|
467
|
+
const sim = await server.simulateTransaction(
|
|
468
|
+
rawTx,
|
|
469
|
+
undefined, // addlResources
|
|
470
|
+
'record_allow_nonroot', // ← This enables non-root auth recording!
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
if (rpc.Api.isSimulationError(sim)) {
|
|
474
|
+
throw new Error(`Simulation failed: ${JSON.stringify(sim)}`);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
console.log('✅ Simulation complete');
|
|
478
|
+
console.log(' Auth entries returned:', sim.result?.auth?.length ?? 0);
|
|
479
|
+
|
|
480
|
+
// 3. Sign auth entries
|
|
481
|
+
const latestLedger = sim.latestLedger;
|
|
482
|
+
const validUntilLedger = latestLedger + 100;
|
|
483
|
+
const networkId = hash(Buffer.from(networkPassphrase));
|
|
484
|
+
|
|
485
|
+
if (sim.result && sim.result.auth) {
|
|
486
|
+
sim.result.auth = await Promise.all(
|
|
487
|
+
sim.result.auth.map(async (entry) => {
|
|
488
|
+
const credentials = entry.credentials();
|
|
489
|
+
|
|
490
|
+
// Source account credentials are already signed by tx envelope
|
|
491
|
+
if (
|
|
492
|
+
credentials.switch() ===
|
|
493
|
+
xdr.SorobanCredentialsType.sorobanCredentialsSourceAccount()
|
|
494
|
+
) {
|
|
495
|
+
console.log(' Skipping source account auth entry');
|
|
496
|
+
return entry;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Address credentials need explicit signature
|
|
500
|
+
const addressCred = credentials.address();
|
|
501
|
+
const addr = Address.fromScAddress(addressCred.address()).toString();
|
|
502
|
+
const rootInvocation = entry.rootInvocation();
|
|
503
|
+
|
|
504
|
+
console.log(' Processing auth entry for address:', addr);
|
|
505
|
+
console.log(' Auth entry tree:');
|
|
506
|
+
logInvocationTree(rootInvocation, 2);
|
|
507
|
+
|
|
508
|
+
// Check if this is the executor's auth entry (Abstract Account)
|
|
509
|
+
if (addr === executorAddr.toString()) {
|
|
510
|
+
console.log(' ✍️ Signing executor auth entry (Abstract Account)...');
|
|
511
|
+
|
|
512
|
+
// Compute the signature_payload hash
|
|
513
|
+
const preimage = xdr.HashIdPreimage.envelopeTypeSorobanAuthorization(
|
|
514
|
+
new xdr.HashIdPreimageSorobanAuthorization({
|
|
515
|
+
networkId,
|
|
516
|
+
nonce: addressCred.nonce(),
|
|
517
|
+
signatureExpirationLedger: validUntilLedger,
|
|
518
|
+
invocation: rootInvocation,
|
|
519
|
+
}),
|
|
520
|
+
);
|
|
521
|
+
const signaturePayload = hash(preimage.toXDR());
|
|
522
|
+
|
|
523
|
+
// Sign the signature_payload with admin's Ed25519 key
|
|
524
|
+
const adminSignature = adminKeypair.sign(signaturePayload);
|
|
525
|
+
|
|
526
|
+
// Build ExecutorSignature struct as ScVal
|
|
527
|
+
// struct ExecutorSignature { public_key: BytesN<32>, signature: BytesN<64> }
|
|
528
|
+
const executorSignatureScVal = xdr.ScVal.scvMap([
|
|
529
|
+
new xdr.ScMapEntry({
|
|
530
|
+
key: xdr.ScVal.scvSymbol('public_key'),
|
|
531
|
+
val: xdr.ScVal.scvBytes(adminKeypair.rawPublicKey()),
|
|
532
|
+
}),
|
|
533
|
+
new xdr.ScMapEntry({
|
|
534
|
+
key: xdr.ScVal.scvSymbol('signature'),
|
|
535
|
+
val: xdr.ScVal.scvBytes(adminSignature),
|
|
536
|
+
}),
|
|
537
|
+
]);
|
|
538
|
+
|
|
539
|
+
// Return executor's auth entry with ExecutorSignature
|
|
540
|
+
const newCred = new xdr.SorobanAddressCredentials({
|
|
541
|
+
address: addressCred.address(),
|
|
542
|
+
nonce: addressCred.nonce(),
|
|
543
|
+
signatureExpirationLedger: validUntilLedger,
|
|
544
|
+
signature: executorSignatureScVal,
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
return new xdr.SorobanAuthorizationEntry({
|
|
548
|
+
credentials: xdr.SorobanCredentials.sorobanCredentialsAddress(newCred),
|
|
549
|
+
rootInvocation,
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Check if this is the admin's auth entry (regular Stellar account)
|
|
554
|
+
// This happens when admin needs to authorize token transfers (e.g., native_drop, value transfers)
|
|
555
|
+
if (addr === adminKeypair.publicKey()) {
|
|
556
|
+
console.log(' ✍️ Signing admin auth entry (regular account)...');
|
|
557
|
+
return authorizeEntry(entry, adminKeypair, validUntilLedger, networkPassphrase);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
throw new Error(`Unexpected auth signer needed: ${addr}`);
|
|
561
|
+
}),
|
|
562
|
+
);
|
|
563
|
+
console.log('✅ Auth entries signed');
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// 4. Assemble transaction with signed auth entries
|
|
567
|
+
const txWithSignedAuth = rpc.assembleTransaction(rawTx, sim).build();
|
|
568
|
+
|
|
569
|
+
console.log('✅ Transaction assembled with signed auth entries');
|
|
570
|
+
|
|
571
|
+
// 5. Re-simulate to get correct footprint (includes __check_auth storage accesses)
|
|
572
|
+
const finalSim = await server.simulateTransaction(txWithSignedAuth);
|
|
573
|
+
if (rpc.Api.isSimulationError(finalSim)) {
|
|
574
|
+
throw new Error(`Final simulation failed: ${JSON.stringify(finalSim)}`);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
console.log('✅ Final simulation completed');
|
|
578
|
+
|
|
579
|
+
// 6. Assemble final transaction with accurate footprint
|
|
580
|
+
const assembledFinalTx = rpc.assembleTransaction(txWithSignedAuth, finalSim).build();
|
|
581
|
+
|
|
582
|
+
// 7. Rebuild the transaction with adminKeypair as the source account
|
|
583
|
+
// The original transaction was built with DEFAULT_DEPLOYER as source,
|
|
584
|
+
// but we want EXECUTOR_ADMIN to be the source so they can sign the envelope
|
|
585
|
+
// Fetch the current sequence number for the admin account from the network
|
|
586
|
+
const adminAccountInfo = await server.getAccount(adminKeypair.publicKey());
|
|
587
|
+
const adminAccount = new Account(adminKeypair.publicKey(), adminAccountInfo.sequenceNumber());
|
|
588
|
+
const finalTxBuilder = new TransactionBuilder(adminAccount, {
|
|
589
|
+
fee: assembledFinalTx.fee,
|
|
590
|
+
networkPassphrase,
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
// Get the transaction XDR to extract the operation and soroban data
|
|
594
|
+
const txXdr = assembledFinalTx.toEnvelope().v1().tx();
|
|
595
|
+
|
|
596
|
+
// Copy the Soroban invoke operation (there's only one operation in Soroban transactions)
|
|
597
|
+
const operationXdr = txXdr.operations()[0];
|
|
598
|
+
finalTxBuilder.addOperation(xdr.Operation.fromXDR(operationXdr.toXDR()));
|
|
599
|
+
|
|
600
|
+
// Copy the Soroban transaction data (footprint, resources, etc.)
|
|
601
|
+
const extSwitch = txXdr.ext().switch();
|
|
602
|
+
if (extSwitch === 1) {
|
|
603
|
+
// Has SorobanTransactionData
|
|
604
|
+
finalTxBuilder.setSorobanData(txXdr.ext().sorobanData());
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Set timeout to match original
|
|
608
|
+
finalTxBuilder.setTimeout(30);
|
|
609
|
+
|
|
610
|
+
const finalTx = finalTxBuilder.build();
|
|
611
|
+
|
|
612
|
+
// 8. Sign the transaction envelope
|
|
613
|
+
finalTx.sign(adminKeypair);
|
|
614
|
+
|
|
615
|
+
console.log('✅ Transaction envelope signed');
|
|
616
|
+
|
|
617
|
+
// 9. Send and poll
|
|
618
|
+
const sentResult = await server.sendTransaction(finalTx);
|
|
619
|
+
|
|
620
|
+
if (sentResult.status !== 'PENDING') {
|
|
621
|
+
throw new Error(`Transaction failed to send: ${JSON.stringify(sentResult)}`);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
console.log('✅ Transaction sent, hash:', sentResult.hash);
|
|
625
|
+
|
|
626
|
+
const txResult = await server.pollTransaction(sentResult.hash);
|
|
627
|
+
|
|
628
|
+
if (txResult.status !== 'SUCCESS') {
|
|
629
|
+
throw new Error(`Transaction failed: ${JSON.stringify(txResult)}`);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
console.log('✅ Transaction completed successfully');
|
|
633
|
+
|
|
634
|
+
return txResult as rpc.Api.GetSuccessfulTransactionResponse;
|
|
157
635
|
}
|
|
158
636
|
|
|
159
637
|
/**
|
|
@@ -182,18 +660,13 @@ function logInvocationTree(invocation: xdr.SorobanAuthorizedInvocation, depth: n
|
|
|
182
660
|
}
|
|
183
661
|
}
|
|
184
662
|
|
|
185
|
-
export function
|
|
186
|
-
|
|
663
|
+
export function assertTransactionSucceeded(
|
|
664
|
+
txResult: rpc.Api.GetTransactionResponse,
|
|
187
665
|
contextLabel: string,
|
|
188
666
|
): void {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const status = txResponse ? txResponse.status : 'UNKNOWN';
|
|
194
|
-
throw new Error(
|
|
195
|
-
`Transaction ${contextLabel} failed with status ${status}. Response: ${JSON.stringify(txResponse)}`,
|
|
196
|
-
);
|
|
197
|
-
}
|
|
667
|
+
if (txResult.status !== rpc.Api.GetTransactionStatus.SUCCESS) {
|
|
668
|
+
throw new Error(
|
|
669
|
+
`Transaction ${contextLabel} failed with status ${txResult.status}. Response: ${JSON.stringify(txResult)}`,
|
|
670
|
+
);
|
|
198
671
|
}
|
|
199
672
|
}
|