diff --git a/bun.lock b/bun.lock index 5937e973..0c0e556e 100644 --- a/bun.lock +++ b/bun.lock @@ -71,6 +71,7 @@ "@bufbuild/protobuf": "2.6.3", "@bufbuild/protoc-gen-es": "2.6.3", "@chainlink/cre-sdk-javy-plugin": "workspace:*", + "@noble/curves": "2.2.0", "@noble/hashes": "2.2.0", "@solana/addresses": "6.10.0", "@solana/codecs": "6.10.0", @@ -82,7 +83,7 @@ "@biomejs/biome": "2.3.14", "@bufbuild/buf": "1.56.0", "@types/bun": "1.3.8", - "chain-selectors": "https://github.com/smartcontractkit/chain-selectors.git#3e718d1c9910b2952c50befe378b0fe8707e6647", + "chain-selectors": "https://github.com/smartcontractkit/chain-selectors.git#7d1e9798b28404818eba5246383c0688c4ee0e1f", "fast-glob": "3.3.3", "ts-proto": "2.7.5", "typescript": "5.9.3", @@ -177,7 +178,7 @@ "@noble/ciphers": ["@noble/ciphers@1.3.0", "", {}, "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw=="], - "@noble/curves": ["@noble/curves@1.9.1", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA=="], + "@noble/curves": ["@noble/curves@2.2.0", "", { "dependencies": { "@noble/hashes": "2.2.0" } }, "sha512-T/BoHgFXirb0ENSPBquzX0rcjXeM6Lo892a2jlYJkqk83LqZx0l1Of7DzlKJ6jkpvMrkHSnAcgb5JegL8SeIkQ=="], "@noble/hashes": ["@noble/hashes@2.2.0", "", {}, "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg=="], @@ -219,7 +220,7 @@ "@types/bun": ["@types/bun@1.3.8", "", { "dependencies": { "bun-types": "1.3.8" } }, "sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA=="], - "@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="], + "@types/node": ["@types/node@26.1.1", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw=="], "@typescript/vfs": ["@typescript/vfs@1.6.4", "", { "dependencies": { "debug": "^4.4.3" }, "peerDependencies": { "typescript": "*" } }, "sha512-PJFXFS4ZJKiJ9Qiuix6Dz/OwEIqHD7Dme1UwZhTK11vR+5dqW2ACbdndWQexBzCx+CPuMe5WBYQWCsFyGlQLlQ=="], @@ -229,7 +230,7 @@ "bun-types": ["bun-types@1.3.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q=="], - "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], + "call-bind": ["call-bind@1.0.9", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" } }, "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ=="], "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], @@ -237,7 +238,7 @@ "case-anything": ["case-anything@2.1.13", "", {}, "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng=="], - "chain-selectors": ["chain-selectors@github:smartcontractkit/chain-selectors#3e718d1", {}, "smartcontractkit-chain-selectors-3e718d1"], + "chain-selectors": ["chain-selectors@github:smartcontractkit/chain-selectors#7d1e979", {}, "smartcontractkit-chain-selectors-7d1e979"], "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], @@ -257,7 +258,7 @@ "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], - "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + "es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="], "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], @@ -281,7 +282,7 @@ "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], - "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], @@ -343,7 +344,7 @@ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], - "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="], + "undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="], "uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="], @@ -361,16 +362,18 @@ "@chainlink/cre-sdk-examples/viem": ["viem@2.34.0", "", { "dependencies": { "@noble/curves": "1.9.6", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.0.8", "isows": "1.0.7", "ox": "0.8.7", "ws": "8.18.3" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-HJZG9Wt0DLX042MG0PK17tpataxtdAEhpta9/Q44FqKwy3xZMI5Lx4jF+zZPuXFuYjZ68R0PXqRwlswHs6r4gA=="], - "@noble/curves/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], - "@scure/bip32/@noble/curves": ["@noble/curves@1.9.6", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-GIKz/j99FRthB8icyJQA51E8Uk5hXmdyThjgQXRKiv9h0zeRlzSCLIzFw6K1LotZ3XuB7yzlf76qk7uBmTdFqA=="], "@scure/bip32/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], "@scure/bip39/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + "ox/@noble/curves": ["@noble/curves@1.9.1", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA=="], + "ox/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + "viem/@noble/curves": ["@noble/curves@1.9.1", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA=="], + "viem/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], "@chainlink/cre-sdk-examples/viem/@noble/curves": ["@noble/curves@1.9.6", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-GIKz/j99FRthB8icyJQA51E8Uk5hXmdyThjgQXRKiv9h0zeRlzSCLIzFw6K1LotZ3XuB7yzlf76qk7uBmTdFqA=="], diff --git a/packages/cre-sdk/package.json b/packages/cre-sdk/package.json index 60bfcccd..5b84bfbb 100644 --- a/packages/cre-sdk/package.json +++ b/packages/cre-sdk/package.json @@ -63,6 +63,7 @@ "@bufbuild/protoc-gen-es": "2.6.3", "@chainlink/cre-sdk-javy-plugin": "workspace:*", "@noble/hashes": "2.2.0", + "@noble/curves": "2.2.0", "@solana/addresses": "6.10.0", "@solana/codecs": "6.10.0", "@standard-schema/spec": "1.0.0", @@ -73,7 +74,7 @@ "@biomejs/biome": "2.3.14", "@bufbuild/buf": "1.56.0", "@types/bun": "1.3.8", - "chain-selectors": "https://github.com/smartcontractkit/chain-selectors.git#3e718d1c9910b2952c50befe378b0fe8707e6647", + "chain-selectors": "https://github.com/smartcontractkit/chain-selectors.git#7d1e9798b28404818eba5246383c0688c4ee0e1f", "fast-glob": "3.3.3", "ts-proto": "2.7.5", "typescript": "5.9.3", diff --git a/packages/cre-sdk/src/generated/chain-selectors/testnet/evm/glamsterdam-devnet-6.ts b/packages/cre-sdk/src/generated/chain-selectors/testnet/evm/glamsterdam-devnet-6.ts new file mode 100644 index 00000000..4da958d1 --- /dev/null +++ b/packages/cre-sdk/src/generated/chain-selectors/testnet/evm/glamsterdam-devnet-6.ts @@ -0,0 +1,16 @@ +// This file is auto-generated. Do not edit manually. +// Generated from: https://github.com/smartcontractkit/chain-selectors + +import type { NetworkInfo } from '@cre/sdk/utils/chain-selectors/types' + +const network: NetworkInfo = { + chainId: '7052886157', + chainSelector: { + name: 'glamsterdam-devnet-6', + selector: 410896468069059699n, + }, + chainFamily: 'evm', + networkType: 'testnet', +} as const + +export default network diff --git a/packages/cre-sdk/src/generated/chain-selectors/testnet/evm/t-rex-testnet.ts b/packages/cre-sdk/src/generated/chain-selectors/testnet/evm/t-rex-testnet.ts new file mode 100644 index 00000000..5bed4c41 --- /dev/null +++ b/packages/cre-sdk/src/generated/chain-selectors/testnet/evm/t-rex-testnet.ts @@ -0,0 +1,16 @@ +// This file is auto-generated. Do not edit manually. +// Generated from: https://github.com/smartcontractkit/chain-selectors + +import type { NetworkInfo } from '@cre/sdk/utils/chain-selectors/types' + +const network: NetworkInfo = { + chainId: '364301', + chainSelector: { + name: 't-rex-testnet', + selector: 17611928792452358269n, + }, + chainFamily: 'evm', + networkType: 'testnet', +} as const + +export default network diff --git a/packages/cre-sdk/src/generated/networks.ts b/packages/cre-sdk/src/generated/networks.ts index d5f19dc8..f6371906 100644 --- a/packages/cre-sdk/src/generated/networks.ts +++ b/packages/cre-sdk/src/generated/networks.ts @@ -216,6 +216,7 @@ import testnet_evm_gate_chain_testnet_meteora from './chain-selectors/testnet/ev import testnet_evm_gate_layer_testnet from './chain-selectors/testnet/evm/gate-layer-testnet' import testnet_evm_geth_testnet from './chain-selectors/testnet/evm/geth-testnet' import testnet_evm_glamsterdam_devnet_5 from './chain-selectors/testnet/evm/glamsterdam-devnet-5' +import testnet_evm_glamsterdam_devnet_6 from './chain-selectors/testnet/evm/glamsterdam-devnet-6' import testnet_evm_gnosis_chain_testnet_chiado from './chain-selectors/testnet/evm/gnosis.chain-testnet-chiado' import testnet_evm_hedera_testnet from './chain-selectors/testnet/evm/hedera-testnet' import testnet_evm_hemi_testnet_sepolia from './chain-selectors/testnet/evm/hemi-testnet-sepolia' @@ -268,6 +269,7 @@ import testnet_evm_sonic_testnet_blaze from './chain-selectors/testnet/evm/sonic import testnet_evm_stable_testnet from './chain-selectors/testnet/evm/stable-testnet' import testnet_evm_story_testnet from './chain-selectors/testnet/evm/story-testnet' import testnet_evm_superseed_testnet from './chain-selectors/testnet/evm/superseed-testnet' +import testnet_evm_t_rex_testnet from './chain-selectors/testnet/evm/t-rex-testnet' import testnet_evm_tac_testnet from './chain-selectors/testnet/evm/tac-testnet' import testnet_evm_telos_evm_testnet from './chain-selectors/testnet/evm/telos-evm-testnet' import testnet_evm_tempo_testnet from './chain-selectors/testnet/evm/tempo-testnet' @@ -520,6 +522,7 @@ export const allNetworks: NetworkInfo[] = [ testnet_evm_bitcoin_testnet_bitlayer_1, mainnet_evm_bitcoin_mainnet_bitlayer_1, testnet_evm_ethereum_testnet_sepolia_ronin_1, + testnet_evm_t_rex_testnet, testnet_evm_ethereum_testnet_goerli_arbitrum_1, testnet_evm_ethereum_testnet_sepolia_arbitrum_1, testnet_evm_private_testnet_mica, @@ -568,6 +571,7 @@ export const allNetworks: NetworkInfo[] = [ testnet_evm_tron_testnet_shasta_evm, testnet_evm_tron_devnet_evm, testnet_evm_tron_testnet_nile_evm, + testnet_evm_glamsterdam_devnet_6, testnet_evm_glamsterdam_devnet_5, mainnet_solana_solana_mainnet, testnet_solana_solana_testnet, @@ -836,6 +840,7 @@ export const testnet = { testnet_evm_mind_testnet, testnet_evm_bitcoin_testnet_bitlayer_1, testnet_evm_ethereum_testnet_sepolia_ronin_1, + testnet_evm_t_rex_testnet, testnet_evm_ethereum_testnet_goerli_arbitrum_1, testnet_evm_ethereum_testnet_sepolia_arbitrum_1, testnet_evm_private_testnet_mica, @@ -872,6 +877,7 @@ export const testnet = { testnet_evm_tron_testnet_shasta_evm, testnet_evm_tron_devnet_evm, testnet_evm_tron_testnet_nile_evm, + testnet_evm_glamsterdam_devnet_6, testnet_evm_glamsterdam_devnet_5, ] as const, solana: [testnet_solana_solana_testnet, testnet_solana_solana_devnet] as const, @@ -1132,6 +1138,7 @@ export const testnetBySelector = new Map([ [7189150270347329685n, testnet_evm_mind_testnet], [3789623672476206327n, testnet_evm_bitcoin_testnet_bitlayer_1], [1091131740251125869n, testnet_evm_ethereum_testnet_sepolia_ronin_1], + [17611928792452358269n, testnet_evm_t_rex_testnet], [6101244977088475029n, testnet_evm_ethereum_testnet_goerli_arbitrum_1], [3478487238524512106n, testnet_evm_ethereum_testnet_sepolia_arbitrum_1], [4489326297382772450n, testnet_evm_private_testnet_mica], @@ -1168,6 +1175,7 @@ export const testnetBySelector = new Map([ [13231703482326770598n, testnet_evm_tron_testnet_shasta_evm], [13231703482326770600n, testnet_evm_tron_devnet_evm], [2052925811360307749n, testnet_evm_tron_testnet_nile_evm], + [410896468069059699n, testnet_evm_glamsterdam_devnet_6], [10073034426865795585n, testnet_evm_glamsterdam_devnet_5], [6302590918974934319n, testnet_solana_solana_testnet], [16423721717087811551n, testnet_solana_solana_devnet], @@ -1438,6 +1446,7 @@ export const testnetByName = new Map([ ['mind-testnet', testnet_evm_mind_testnet], ['bitcoin-testnet-bitlayer-1', testnet_evm_bitcoin_testnet_bitlayer_1], ['ethereum-testnet-sepolia-ronin-1', testnet_evm_ethereum_testnet_sepolia_ronin_1], + ['t-rex-testnet', testnet_evm_t_rex_testnet], ['ethereum-testnet-goerli-arbitrum-1', testnet_evm_ethereum_testnet_goerli_arbitrum_1], ['ethereum-testnet-sepolia-arbitrum-1', testnet_evm_ethereum_testnet_sepolia_arbitrum_1], ['private-testnet-mica', testnet_evm_private_testnet_mica], @@ -1480,6 +1489,7 @@ export const testnetByName = new Map([ ['tron-testnet-shasta-evm', testnet_evm_tron_testnet_shasta_evm], ['tron-devnet-evm', testnet_evm_tron_devnet_evm], ['tron-testnet-nile-evm', testnet_evm_tron_testnet_nile_evm], + ['glamsterdam-devnet-6', testnet_evm_glamsterdam_devnet_6], ['glamsterdam-devnet-5', testnet_evm_glamsterdam_devnet_5], ['solana-testnet', testnet_solana_solana_testnet], ['solana-devnet', testnet_solana_solana_devnet], @@ -1744,6 +1754,7 @@ export const testnetBySelectorByFamily = { [7189150270347329685n, testnet_evm_mind_testnet], [3789623672476206327n, testnet_evm_bitcoin_testnet_bitlayer_1], [1091131740251125869n, testnet_evm_ethereum_testnet_sepolia_ronin_1], + [17611928792452358269n, testnet_evm_t_rex_testnet], [6101244977088475029n, testnet_evm_ethereum_testnet_goerli_arbitrum_1], [3478487238524512106n, testnet_evm_ethereum_testnet_sepolia_arbitrum_1], [4489326297382772450n, testnet_evm_private_testnet_mica], @@ -1780,6 +1791,7 @@ export const testnetBySelectorByFamily = { [13231703482326770598n, testnet_evm_tron_testnet_shasta_evm], [13231703482326770600n, testnet_evm_tron_devnet_evm], [2052925811360307749n, testnet_evm_tron_testnet_nile_evm], + [410896468069059699n, testnet_evm_glamsterdam_devnet_6], [10073034426865795585n, testnet_evm_glamsterdam_devnet_5], ]), solana: new Map([ @@ -2067,6 +2079,7 @@ export const testnetByNameByFamily = { ['mind-testnet', testnet_evm_mind_testnet], ['bitcoin-testnet-bitlayer-1', testnet_evm_bitcoin_testnet_bitlayer_1], ['ethereum-testnet-sepolia-ronin-1', testnet_evm_ethereum_testnet_sepolia_ronin_1], + ['t-rex-testnet', testnet_evm_t_rex_testnet], ['ethereum-testnet-goerli-arbitrum-1', testnet_evm_ethereum_testnet_goerli_arbitrum_1], ['ethereum-testnet-sepolia-arbitrum-1', testnet_evm_ethereum_testnet_sepolia_arbitrum_1], ['private-testnet-mica', testnet_evm_private_testnet_mica], @@ -2109,6 +2122,7 @@ export const testnetByNameByFamily = { ['tron-testnet-shasta-evm', testnet_evm_tron_testnet_shasta_evm], ['tron-devnet-evm', testnet_evm_tron_devnet_evm], ['tron-testnet-nile-evm', testnet_evm_tron_testnet_nile_evm], + ['glamsterdam-devnet-6', testnet_evm_glamsterdam_devnet_6], ['glamsterdam-devnet-5', testnet_evm_glamsterdam_devnet_5], ]), solana: new Map([ diff --git a/packages/cre-sdk/src/sdk/cre/index.ts b/packages/cre-sdk/src/sdk/cre/index.ts index 46388b85..3b9951f6 100644 --- a/packages/cre-sdk/src/sdk/cre/index.ts +++ b/packages/cre-sdk/src/sdk/cre/index.ts @@ -42,8 +42,14 @@ export { TxStatus, } from '@cre/generated/capabilities/blockchain/evm/v1alpha/client_pb' export { + ComparisonOperator as SolanaComparisonOperator, + type CPIFilterConfigJson as SolanaCPIFilterConfigJson, + type FilterLogTriggerRequestJson as SolanaFilterLogTriggerRequestJson, + type Log as SolanaLog, ReceiverContractExecutionStatus as SolanaReceiverContractExecutionStatus, + type SubkeyConfigJson as SolanaSubkeyConfigJson, TxStatus as SolanaTxStatus, + type ValueComparatorJson as SolanaValueComparatorJson, } from '@cre/generated/capabilities/blockchain/solana/v1alpha/client_pb' export type { Payload as HTTPPayload } from '@cre/generated/capabilities/networking/http/v1alpha/trigger_pb' export type { Payload as CronPayload } from '@cre/generated/capabilities/scheduler/cron/v1/trigger_pb' diff --git a/packages/cre-sdk/src/sdk/test/solana-binding-fixture/DataStorage.ts b/packages/cre-sdk/src/sdk/test/solana-binding-fixture/DataStorage.ts index eba4db12..b90a535e 100644 --- a/packages/cre-sdk/src/sdk/test/solana-binding-fixture/DataStorage.ts +++ b/packages/cre-sdk/src/sdk/test/solana-binding-fixture/DataStorage.ts @@ -1,17 +1,28 @@ // Code generated — DO NOT EDIT. import { + adaptTrigger, + anchorCPILogTriggerConfig, + bytesToBase64, bytesToHex, calculateAccountsHash, encodeBorshVecU32, encodeForwarderReport, prepareSolanaReportRequest, + prepareSubkeyValue, type Runtime, type SolanaAccountMeta, SolanaClient, type SolanaComputeConfig, + type SolanaDecodedLog, + type SolanaFilterLogTriggerRequestJson, + type SolanaLog, + type SolanaLogTriggerOptions, + type SolanaSubkeyConfigJson, + type SolanaValueComparatorJson, solanaAccountMetasToJson, solanaAddressToBytes, + type Trigger, } from '@cre/sdk' import { type Address, getAddressCodec } from '@solana/addresses' import { @@ -64,7 +75,10 @@ export const DATA_STORAGE_IDL = { writable: true, pda: { seeds: [ - { kind: 'const', value: [100, 97, 116, 97, 95, 97, 99, 99, 111, 117, 110, 116] }, + { + kind: 'const', + value: [100, 97, 116, 97, 95, 97, 99, 99, 111, 117, 110, 116], + }, { kind: 'account', path: 'user' }, ], }, @@ -90,7 +104,10 @@ export const DATA_STORAGE_IDL = { writable: true, pda: { seeds: [ - { kind: 'const', value: [100, 97, 116, 97, 95, 97, 99, 99, 111, 117, 110, 116] }, + { + kind: 'const', + value: [100, 97, 116, 97, 95, 97, 99, 99, 111, 117, 110, 116], + }, { kind: 'account', path: 'user' }, ], }, @@ -112,7 +129,10 @@ export const DATA_STORAGE_IDL = { writable: true, pda: { seeds: [ - { kind: 'const', value: [100, 97, 116, 97, 95, 97, 99, 99, 111, 117, 110, 116] }, + { + kind: 'const', + value: [100, 97, 116, 97, 95, 97, 99, 99, 111, 117, 110, 116], + }, { kind: 'account', path: 'user' }, ], }, @@ -133,7 +153,10 @@ export const DATA_STORAGE_IDL = { writable: true, pda: { seeds: [ - { kind: 'const', value: [100, 97, 116, 97, 95, 97, 99, 99, 111, 117, 110, 116] }, + { + kind: 'const', + value: [100, 97, 116, 97, 95, 97, 99, 99, 111, 117, 110, 116], + }, { kind: 'account', path: 'user' }, ], }, @@ -144,8 +167,14 @@ export const DATA_STORAGE_IDL = { ], accounts: [{ name: 'DataAccount', discriminator: [85, 240, 182, 158, 76, 7, 18, 233] }], events: [ - { name: 'AccessLogged', discriminator: [243, 53, 225, 71, 64, 120, 109, 25] }, - { name: 'DynamicEvent', discriminator: [236, 145, 224, 161, 9, 222, 218, 237] }, + { + name: 'AccessLogged', + discriminator: [243, 53, 225, 71, 64, 120, 109, 25], + }, + { + name: 'DynamicEvent', + discriminator: [236, 145, 224, 161, 9, 222, 218, 237], + }, { name: 'NoFields', discriminator: [160, 156, 94, 85, 77, 122, 98, 240] }, ], errors: [{ code: 6000, name: 'DataNotFound', msg: 'data not found' }], @@ -208,6 +237,10 @@ export const DATA_STORAGE_IDL = { ], } as const +// Base64 of the compact IDL JSON, passed to log triggers as contractIdlJson. +const DATA_STORAGE_IDL_BASE64 = + 'eyJhZGRyZXNzIjoiRUNMODE0MmoyWVFBdnM5UjlnZVNzUm5rVkgyd0xFaTdzb0pDUnlKNzRjZkwiLCJtZXRhZGF0YSI6eyJuYW1lIjoiZGF0YV9zdG9yYWdlIiwidmVyc2lvbiI6IjAuMS4wIiwic3BlYyI6IjAuMS4wIiwiZGVzY3JpcHRpb24iOiJDcmVhdGVkIHdpdGggQW5jaG9yIn0sImluc3RydWN0aW9ucyI6W3sibmFtZSI6ImdldF9tdWx0aXBsZV9yZXNlcnZlcyIsImRpc2NyaW1pbmF0b3IiOlsxMDQsMTIyLDE0MCwxMDQsMTc1LDE1MSw3MCw0Ml0sImFjY291bnRzIjpbXSwiYXJncyI6W10sInJldHVybnMiOnsidmVjIjp7ImRlZmluZWQiOnsibmFtZSI6IlVwZGF0ZVJlc2VydmVzIn19fX0seyJuYW1lIjoiZ2V0X3Jlc2VydmVzIiwiZGlzY3JpbWluYXRvciI6WzEyMSwxNDAsMjM3LDg0LDIxOCwxMDUsNDgsMTddLCJhY2NvdW50cyI6W10sImFyZ3MiOltdLCJyZXR1cm5zIjp7ImRlZmluZWQiOnsibmFtZSI6IlVwZGF0ZVJlc2VydmVzIn19fSx7Im5hbWUiOiJnZXRfdHVwbGVfcmVzZXJ2ZXMiLCJkaXNjcmltaW5hdG9yIjpbMTg5LDgzLDE4NiwyMCwxMjcsODAsMTA5LDQ5XSwiYWNjb3VudHMiOltdLCJhcmdzIjpbXX0seyJuYW1lIjoiaW5pdGlhbGl6ZV9kYXRhX2FjY291bnQiLCJkaXNjcmltaW5hdG9yIjpbOSw2NCw3OCw0OSw3MSwxOTMsMTUsMjUwXSwiYWNjb3VudHMiOlt7Im5hbWUiOiJkYXRhX2FjY291bnQiLCJ3cml0YWJsZSI6dHJ1ZSwicGRhIjp7InNlZWRzIjpbeyJraW5kIjoiY29uc3QiLCJ2YWx1ZSI6WzEwMCw5NywxMTYsOTcsOTUsOTcsOTksOTksMTExLDExNywxMTAsMTE2XX0seyJraW5kIjoiYWNjb3VudCIsInBhdGgiOiJ1c2VyIn1dfX0seyJuYW1lIjoidXNlciIsIndyaXRhYmxlIjp0cnVlLCJzaWduZXIiOnRydWV9LHsibmFtZSI6InN5c3RlbV9wcm9ncmFtIiwiYWRkcmVzcyI6IjExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExIn1dLCJhcmdzIjpbeyJuYW1lIjoiaW5wdXQiLCJ0eXBlIjp7ImRlZmluZWQiOnsibmFtZSI6IlVzZXJEYXRhIn19fV19LHsibmFtZSI6ImxvZ19hY2Nlc3MiLCJkaXNjcmltaW5hdG9yIjpbMTk2LDU1LDE5NCwyNCw1LDIyNCwxNjEsMjA0XSwiYWNjb3VudHMiOlt7Im5hbWUiOiJ1c2VyIiwic2lnbmVyIjp0cnVlfV0sImFyZ3MiOlt7Im5hbWUiOiJtZXNzYWdlIiwidHlwZSI6InN0cmluZyJ9XX0seyJuYW1lIjoib25fcmVwb3J0IiwiZGlzY3JpbWluYXRvciI6WzIxNCwxNzMsMTgsMjIxLDE3MywxNDgsMTUxLDIwOF0sImFjY291bnRzIjpbeyJuYW1lIjoidXNlciIsIndyaXRhYmxlIjp0cnVlLCJzaWduZXIiOnRydWV9LHsibmFtZSI6ImRhdGFfYWNjb3VudCIsIndyaXRhYmxlIjp0cnVlLCJwZGEiOnsic2VlZHMiOlt7ImtpbmQiOiJjb25zdCIsInZhbHVlIjpbMTAwLDk3LDExNiw5Nyw5NSw5Nyw5OSw5OSwxMTEsMTE3LDExMCwxMTZdfSx7ImtpbmQiOiJhY2NvdW50IiwicGF0aCI6InVzZXIifV19fSx7Im5hbWUiOiJzeXN0ZW1fcHJvZ3JhbSIsImFkZHJlc3MiOiIxMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMSJ9XSwiYXJncyI6W3sibmFtZSI6Il9tZXRhZGF0YSIsInR5cGUiOiJieXRlcyJ9LHsibmFtZSI6InBheWxvYWQiLCJ0eXBlIjoiYnl0ZXMifV19LHsibmFtZSI6InVwZGF0ZV9rZXlfdmFsdWVfZGF0YSIsImRpc2NyaW1pbmF0b3IiOls2NywxMzcsMTQ0LDM1LDIxMCwxMjYsMjU0LDc5XSwiYWNjb3VudHMiOlt7Im5hbWUiOiJ1c2VyIiwid3JpdGFibGUiOnRydWUsInNpZ25lciI6dHJ1ZX0seyJuYW1lIjoiZGF0YV9hY2NvdW50Iiwid3JpdGFibGUiOnRydWUsInBkYSI6eyJzZWVkcyI6W3sia2luZCI6ImNvbnN0IiwidmFsdWUiOlsxMDAsOTcsMTE2LDk3LDk1LDk3LDk5LDk5LDExMSwxMTcsMTEwLDExNl19LHsia2luZCI6ImFjY291bnQiLCJwYXRoIjoidXNlciJ9XX19XSwiYXJncyI6W3sibmFtZSI6ImtleSIsInR5cGUiOiJzdHJpbmcifSx7Im5hbWUiOiJ2YWx1ZSIsInR5cGUiOiJzdHJpbmcifV19LHsibmFtZSI6InVwZGF0ZV91c2VyX2RhdGEiLCJkaXNjcmltaW5hdG9yIjpbMTEsMTMsMTE0LDE1MCwxOTQsMjI0LDE5Miw3OF0sImFjY291bnRzIjpbeyJuYW1lIjoidXNlciIsIndyaXRhYmxlIjp0cnVlLCJzaWduZXIiOnRydWV9LHsibmFtZSI6ImRhdGFfYWNjb3VudCIsIndyaXRhYmxlIjp0cnVlLCJwZGEiOnsic2VlZHMiOlt7ImtpbmQiOiJjb25zdCIsInZhbHVlIjpbMTAwLDk3LDExNiw5Nyw5NSw5Nyw5OSw5OSwxMTEsMTE3LDExMCwxMTZdfSx7ImtpbmQiOiJhY2NvdW50IiwicGF0aCI6InVzZXIifV19fV0sImFyZ3MiOlt7Im5hbWUiOiJpbnB1dCIsInR5cGUiOnsiZGVmaW5lZCI6eyJuYW1lIjoiVXNlckRhdGEifX19XX1dLCJhY2NvdW50cyI6W3sibmFtZSI6IkRhdGFBY2NvdW50IiwiZGlzY3JpbWluYXRvciI6Wzg1LDI0MCwxODIsMTU4LDc2LDcsMTgsMjMzXX1dLCJldmVudHMiOlt7Im5hbWUiOiJBY2Nlc3NMb2dnZWQiLCJkaXNjcmltaW5hdG9yIjpbMjQzLDUzLDIyNSw3MSw2NCwxMjAsMTA5LDI1XX0seyJuYW1lIjoiRHluYW1pY0V2ZW50IiwiZGlzY3JpbWluYXRvciI6WzIzNiwxNDUsMjI0LDE2MSw5LDIyMiwyMTgsMjM3XX0seyJuYW1lIjoiTm9GaWVsZHMiLCJkaXNjcmltaW5hdG9yIjpbMTYwLDE1Niw5NCw4NSw3NywxMjIsOTgsMjQwXX1dLCJlcnJvcnMiOlt7ImNvZGUiOjYwMDAsIm5hbWUiOiJEYXRhTm90Rm91bmQiLCJtc2ciOiJkYXRhIG5vdCBmb3VuZCJ9XSwidHlwZXMiOlt7Im5hbWUiOiJBY2Nlc3NMb2dnZWQiLCJ0eXBlIjp7ImtpbmQiOiJzdHJ1Y3QiLCJmaWVsZHMiOlt7Im5hbWUiOiJjYWxsZXIiLCJ0eXBlIjoicHVia2V5In0seyJuYW1lIjoibWVzc2FnZSIsInR5cGUiOiJzdHJpbmcifV19fSx7Im5hbWUiOiJEYXRhQWNjb3VudCIsInR5cGUiOnsia2luZCI6InN0cnVjdCIsImZpZWxkcyI6W3sibmFtZSI6InNlbmRlciIsInR5cGUiOiJzdHJpbmcifSx7Im5hbWUiOiJrZXkiLCJ0eXBlIjoic3RyaW5nIn0seyJuYW1lIjoidmFsdWUiLCJ0eXBlIjoic3RyaW5nIn1dfX0seyJuYW1lIjoiRHluYW1pY0V2ZW50IiwidHlwZSI6eyJraW5kIjoic3RydWN0IiwiZmllbGRzIjpbeyJuYW1lIjoia2V5IiwidHlwZSI6InN0cmluZyJ9LHsibmFtZSI6InVzZXJfZGF0YSIsInR5cGUiOnsiZGVmaW5lZCI6eyJuYW1lIjoiVXNlckRhdGEifX19LHsibmFtZSI6InNlbmRlciIsInR5cGUiOiJzdHJpbmcifSx7Im5hbWUiOiJtZXRhZGF0YSIsInR5cGUiOiJieXRlcyJ9LHsibmFtZSI6Im1ldGFkYXRhX2FycmF5IiwidHlwZSI6eyJ2ZWMiOiJieXRlcyJ9fV19fSx7Im5hbWUiOiJOb0ZpZWxkcyIsInR5cGUiOnsia2luZCI6InN0cnVjdCIsImZpZWxkcyI6W119fSx7Im5hbWUiOiJVcGRhdGVSZXNlcnZlcyIsInR5cGUiOnsia2luZCI6InN0cnVjdCIsImZpZWxkcyI6W3sibmFtZSI6InRvdGFsX21pbnRlZCIsInR5cGUiOiJ1NjQifSx7Im5hbWUiOiJ0b3RhbF9yZXNlcnZlIiwidHlwZSI6InU2NCJ9XX19LHsibmFtZSI6IlVzZXJEYXRhIiwidHlwZSI6eyJraW5kIjoic3RydWN0IiwiZmllbGRzIjpbeyJuYW1lIjoia2V5IiwidHlwZSI6InN0cmluZyJ9LHsibmFtZSI6InZhbHVlIiwidHlwZSI6InN0cmluZyJ9XX19XX0=' + const DISCRIMINATOR_SIZE = 8 const expectDiscriminator = (label: string, expected: Uint8Array, data: Uint8Array): Uint8Array => { @@ -269,7 +302,9 @@ export const dynamicEventCodec = getStructCodec([ ['metadata', addCodecSizePrefix(getBytesCodec(), getU32Codec())], [ 'metadataArray', - getArrayCodec(addCodecSizePrefix(getBytesCodec(), getU32Codec()), { size: getU32Codec() }), + getArrayCodec(addCodecSizePrefix(getBytesCodec(), getU32Codec()), { + size: getU32Codec(), + }), ], ]) @@ -334,6 +369,122 @@ export const decodeNoFieldsEvent = (data: Uint8Array): NoFields => expectDiscriminator('event NoFields', EVENT_NO_FIELDS_DISCRIMINATOR, data), ) as NoFields +export const parseAnyAccount = (data: Uint8Array): DataAccount => { + const disc = data.subarray(0, DISCRIMINATOR_SIZE) + const matches = (expected: Uint8Array) => expected.every((b, i) => disc[i] === b) + if (matches(ACCOUNT_DATA_ACCOUNT_DISCRIMINATOR)) return decodeDataAccountAccount(data) + throw new Error(`unknown account discriminator: [${Array.from(disc).join(', ')}]`) +} + +export const parseAnyEvent = (data: Uint8Array): AccessLogged | DynamicEvent | NoFields => { + const disc = data.subarray(0, DISCRIMINATOR_SIZE) + const matches = (expected: Uint8Array) => expected.every((b, i) => disc[i] === b) + if (matches(EVENT_ACCESS_LOGGED_DISCRIMINATOR)) return decodeAccessLoggedEvent(data) + if (matches(EVENT_DYNAMIC_EVENT_DISCRIMINATOR)) return decodeDynamicEventEvent(data) + if (matches(EVENT_NO_FIELDS_DISCRIMINATOR)) return decodeNoFieldsEvent(data) + throw new Error(`unknown event discriminator: [${Array.from(disc).join(', ')}]`) +} + +/** + * Optional filter values for AccessLogged log triggers. Set a field to filter on + * that value (OR across filter rows). Leave unset for wildcard. Only top-level + * scalar fields with supported subkey encodings are auto-filterable — nested + * structs, vecs, arrays, bool, u128, and i128 need a manual SubkeyConfig. + */ +export type AccessLoggedFilters = { + caller?: Address | null + message?: string | null +} + +export const encodeAccessLoggedSubkeys = ( + filters: AccessLoggedFilters[], +): SolanaSubkeyConfigJson[] => { + const callerComparers: SolanaValueComparatorJson[] = [] + const messageComparers: SolanaValueComparatorJson[] = [] + for (const f of filters) { + if (f.caller != null) { + callerComparers.push({ + operator: 'COMPARISON_OPERATOR_EQ', + value: bytesToBase64(solanaAddressToBytes(f.caller)), + }) + } + if (f.message != null) { + messageComparers.push({ + operator: 'COMPARISON_OPERATOR_EQ', + value: bytesToBase64(prepareSubkeyValue(f.message)), + }) + } + } + const subkeys: SolanaSubkeyConfigJson[] = [] + if (callerComparers.length > 0) { + subkeys.push({ path: ['Caller'], comparers: callerComparers }) + } + if (messageComparers.length > 0) { + subkeys.push({ path: ['Message'], comparers: messageComparers }) + } + return subkeys +} + +/** + * Optional filter values for DynamicEvent log triggers. Set a field to filter on + * that value (OR across filter rows). Leave unset for wildcard. Only top-level + * scalar fields with supported subkey encodings are auto-filterable — nested + * structs, vecs, arrays, bool, u128, and i128 need a manual SubkeyConfig. + */ +export type DynamicEventFilters = { + key?: string | null + sender?: string | null + metadata?: Uint8Array | null +} + +export const encodeDynamicEventSubkeys = ( + filters: DynamicEventFilters[], +): SolanaSubkeyConfigJson[] => { + const keyComparers: SolanaValueComparatorJson[] = [] + const senderComparers: SolanaValueComparatorJson[] = [] + const metadataComparers: SolanaValueComparatorJson[] = [] + for (const f of filters) { + if (f.key != null) { + keyComparers.push({ + operator: 'COMPARISON_OPERATOR_EQ', + value: bytesToBase64(prepareSubkeyValue(f.key)), + }) + } + if (f.sender != null) { + senderComparers.push({ + operator: 'COMPARISON_OPERATOR_EQ', + value: bytesToBase64(prepareSubkeyValue(f.sender)), + }) + } + if (f.metadata != null) { + metadataComparers.push({ + operator: 'COMPARISON_OPERATOR_EQ', + value: bytesToBase64(prepareSubkeyValue(f.metadata)), + }) + } + } + const subkeys: SolanaSubkeyConfigJson[] = [] + if (keyComparers.length > 0) { + subkeys.push({ path: ['Key'], comparers: keyComparers }) + } + if (senderComparers.length > 0) { + subkeys.push({ path: ['Sender'], comparers: senderComparers }) + } + if (metadataComparers.length > 0) { + subkeys.push({ path: ['Metadata'], comparers: metadataComparers }) + } + return subkeys +} + +/** + * Optional filter values for NoFields log triggers. Set a field to filter on + * that value (OR across filter rows). Leave unset for wildcard. Only top-level + * scalar fields with supported subkey encodings are auto-filterable — nested + * structs, vecs, arrays, bool, u128, and i128 need a manual SubkeyConfig. + */ +export type NoFieldsFilters = Record + +export const encodeNoFieldsSubkeys = (_filters: NoFieldsFilters[]): SolanaSubkeyConfigJson[] => [] export class DataStorage { readonly programId: Uint8Array @@ -573,4 +724,82 @@ export class DataStorage { computeConfig, ) } + + /** + * Registers a typed log trigger for AccessLogged events. The trigger + * output is adapted to the decoded AccessLogged data alongside the raw log. + * Pass opts.cpi for events emitted via Anchor's emit_cpi!. + */ + logTriggerAccessLoggedLog( + filterName: string, + filters: AccessLoggedFilters[] = [], + opts?: SolanaLogTriggerOptions, + ): Trigger> { + const config: SolanaFilterLogTriggerRequestJson = { + name: filterName, + address: bytesToBase64(this.programId), + eventName: 'AccessLogged', + contractIdlJson: DATA_STORAGE_IDL_BASE64, + subkeys: encodeAccessLoggedSubkeys(filters), + } + if (opts?.cpi) { + config.cpiFilterConfig = anchorCPILogTriggerConfig(this.programId) + } + return adaptTrigger(this.client.logTrigger(config), (log) => ({ + log, + data: decodeAccessLoggedEvent(log.data), + })) + } + + /** + * Registers a typed log trigger for DynamicEvent events. The trigger + * output is adapted to the decoded DynamicEvent data alongside the raw log. + * Pass opts.cpi for events emitted via Anchor's emit_cpi!. + */ + logTriggerDynamicEventLog( + filterName: string, + filters: DynamicEventFilters[] = [], + opts?: SolanaLogTriggerOptions, + ): Trigger> { + const config: SolanaFilterLogTriggerRequestJson = { + name: filterName, + address: bytesToBase64(this.programId), + eventName: 'DynamicEvent', + contractIdlJson: DATA_STORAGE_IDL_BASE64, + subkeys: encodeDynamicEventSubkeys(filters), + } + if (opts?.cpi) { + config.cpiFilterConfig = anchorCPILogTriggerConfig(this.programId) + } + return adaptTrigger(this.client.logTrigger(config), (log) => ({ + log, + data: decodeDynamicEventEvent(log.data), + })) + } + + /** + * Registers a typed log trigger for NoFields events. The trigger + * output is adapted to the decoded NoFields data alongside the raw log. + * Pass opts.cpi for events emitted via Anchor's emit_cpi!. + */ + logTriggerNoFieldsLog( + filterName: string, + filters: NoFieldsFilters[] = [], + opts?: SolanaLogTriggerOptions, + ): Trigger> { + const config: SolanaFilterLogTriggerRequestJson = { + name: filterName, + address: bytesToBase64(this.programId), + eventName: 'NoFields', + contractIdlJson: DATA_STORAGE_IDL_BASE64, + subkeys: encodeNoFieldsSubkeys(filters), + } + if (opts?.cpi) { + config.cpiFilterConfig = anchorCPILogTriggerConfig(this.programId) + } + return adaptTrigger(this.client.logTrigger(config), (log) => ({ + log, + data: decodeNoFieldsEvent(log.data), + })) + } } diff --git a/packages/cre-sdk/src/sdk/test/solana-binding-fixture/workflow-wasm-check.ts b/packages/cre-sdk/src/sdk/test/solana-binding-fixture/workflow-wasm-check.ts index 9d1885e1..8955ea20 100644 --- a/packages/cre-sdk/src/sdk/test/solana-binding-fixture/workflow-wasm-check.ts +++ b/packages/cre-sdk/src/sdk/test/solana-binding-fixture/workflow-wasm-check.ts @@ -7,9 +7,12 @@ */ import { BasicCapability as BasicTriggerCapability } from '@cre/generated-sdk/capabilities/internal/basictrigger/v1/basic_sdk_gen' import { cre, type Runtime, SolanaClient } from '@cre/sdk/cre' -import { solanaAccountMeta } from '@cre/sdk/utils/capabilities/blockchain/solana/solana-helpers' +import { + type SolanaDecodedLog, + solanaAccountMeta, +} from '@cre/sdk/utils/capabilities/blockchain/solana/solana-helpers' import { Runner } from '@cre/sdk/wasm' -import { DataStorage, type UserData } from './DataStorage' +import { type AccessLogged, DataStorage, type UserData } from './DataStorage' const CHAIN_SELECTOR = SolanaClient.SUPPORTED_CHAIN_SELECTORS['solana-devnet'] @@ -27,9 +30,23 @@ const writeUserData = (runtime: Runtime) => { return 'ok' } +const onAccessLogged = (runtime: Runtime, decoded: SolanaDecodedLog) => { + runtime.log(`caller=${decoded.data.caller} slot=${decoded.log.blockNumber}`) + return 'ok' +} + const initWorkflow = () => { const basicTrigger = new BasicTriggerCapability() - return [cre.handler(basicTrigger.trigger({}), writeUserData)] + const binding = new DataStorage(new SolanaClient(CHAIN_SELECTOR)) + return [ + cre.handler(basicTrigger.trigger({}), writeUserData), + cre.handler( + binding.logTriggerAccessLoggedLog('access-logged-filter', [{ message: 'hello' }], { + cpi: true, + }), + onAccessLogged, + ), + ] } export async function main() { diff --git a/packages/cre-sdk/src/sdk/utils/capabilities/blockchain/solana/solana-helpers.test.ts b/packages/cre-sdk/src/sdk/utils/capabilities/blockchain/solana/solana-helpers.test.ts index aeb20002..4660b147 100644 --- a/packages/cre-sdk/src/sdk/utils/capabilities/blockchain/solana/solana-helpers.test.ts +++ b/packages/cre-sdk/src/sdk/utils/capabilities/blockchain/solana/solana-helpers.test.ts @@ -1,10 +1,13 @@ import { describe, expect, test } from 'bun:test' import { sha256 } from '@noble/hashes/sha2.js' import { + anchorCPILogTriggerConfig, calculateAccountsHash, encodeBorshVecU32, encodeForwarderReport, prepareSolanaReportRequest, + prepareSubkeyFloatValue, + prepareSubkeyValue, SOLANA_DEFAULT_REPORT_ENCODER, solanaAccountMeta, solanaAccountMetasToJson, @@ -173,3 +176,72 @@ describe('solanaAddressToBytes / solanaAccountMeta', () => { expect(json).toEqual([{ publicKey: Buffer.from(key).toString('base64'), isWritable: true }]) }) }) + +describe('prepareSubkeyValue', () => { + // Cases mirror Go bindings common_test.go TestPrepareSubkeyValue. + test('string encodes as UTF-8 bytes', () => { + expect(prepareSubkeyValue('hello')).toEqual(new TextEncoder().encode('hello')) + }) + + test('bytes pass through as-is', () => { + const input = new Uint8Array([1, 2, 3]) + expect(prepareSubkeyValue(input)).toBe(input) + }) + + test('bigint encodes as big-endian u64', () => { + expect(bytesToHex(prepareSubkeyValue(1000n))).toBe('00000000000003e8') + }) + + test("negative bigint encodes as two's complement", () => { + expect(bytesToHex(prepareSubkeyValue(-1n))).toBe('ffffffffffffffff') + }) + + test('small integer number widens to 8 bytes', () => { + expect(bytesToHex(prepareSubkeyValue(5))).toBe('0000000000000005') + }) + + test('non-integer number throws, pointing at the float encoder', () => { + expect(() => prepareSubkeyValue(1.5)).toThrow('prepareSubkeyFloatValue') + }) +}) + +describe('prepareSubkeyFloatValue', () => { + // Vectors mirror Go bindings EncodeIndexedValue float encoding: + // positive → float64bits + 2^63, otherwise 2^63 - float64bits (mod 2^64). + test('positive float', () => { + // float64bits(1.5) = 0x3ff8000000000000; + 2^63 + expect(bytesToHex(prepareSubkeyFloatValue(1.5))).toBe('bff8000000000000') + }) + + test('negative float', () => { + // 2^63 - float64bits(-1.5) mod 2^64 + expect(bytesToHex(prepareSubkeyFloatValue(-1.5))).toBe('c008000000000000') + }) + + test('zero maps to the sign bit', () => { + expect(bytesToHex(prepareSubkeyFloatValue(0))).toBe('8000000000000000') + }) + + test('encoding preserves ordering among same-sign values', () => { + const positives = [0.5, 1.5, 2.5, 100].map((v) => bytesToHex(prepareSubkeyFloatValue(v))) + expect([...positives].sort()).toEqual(positives) + const negatives = [-100, -2.5, -1.5, -0.5].map((v) => bytesToHex(prepareSubkeyFloatValue(v))) + expect([...negatives].sort()).toEqual(negatives) + }) +}) + +describe('anchorCPILogTriggerConfig', () => { + // Mirrors Go bindings common_test.go TestAnchorCPILogTriggerConfig. + test('targets the program itself with the anchor:event method', () => { + const programId = sequentialKey(0) + const cfg = anchorCPILogTriggerConfig(programId) + expect(cfg.destAddress).toBe(Buffer.from(programId).toString('base64')) + expect(cfg.methodName).toBe(Buffer.from('anchor:event').toString('base64')) + }) + + test('accepts a base58 program id', () => { + const base58 = 'ECL8142j2YQAvs9R9geSsRnkVH2wLEi7soJCRyJ74cfL' + const cfg = anchorCPILogTriggerConfig(base58) + expect(cfg.destAddress).toBe(Buffer.from(solanaAddressToBytes(base58)).toString('base64')) + }) +}) diff --git a/packages/cre-sdk/src/sdk/utils/capabilities/blockchain/solana/solana-helpers.ts b/packages/cre-sdk/src/sdk/utils/capabilities/blockchain/solana/solana-helpers.ts index b63e2324..3e5c1cd1 100644 --- a/packages/cre-sdk/src/sdk/utils/capabilities/blockchain/solana/solana-helpers.ts +++ b/packages/cre-sdk/src/sdk/utils/capabilities/blockchain/solana/solana-helpers.ts @@ -1,6 +1,8 @@ import type { AccountMetaJson, ComputeConfigJson, + CPIFilterConfigJson, + Log, } from '@cre/generated/capabilities/blockchain/solana/v1alpha/client_pb' import type { ReportRequestJson } from '@cre/generated/sdk/v1alpha/sdk_pb' import { bytesToBase64 } from '@cre/sdk/utils/hex-utils' @@ -161,3 +163,117 @@ export const prepareSolanaReportRequest = ( payload: Uint8Array, reportEncoder: ReportEncoder = SOLANA_DEFAULT_REPORT_ENCODER, ): ReportRequestJson => prepareReportRequestFromBytes(payload, reportEncoder) + +const U64_LENGTH = 8 +const U64_SIGN_BIT = 1n << 63n +const UTF8_ENCODER = new TextEncoder() + +/** Encodes a bigint as a big-endian u64, wrapping modulo 2^64 (two's complement). */ +const u64BE = (value: bigint): Uint8Array => { + const bytes = new Uint8Array(U64_LENGTH) + new DataView(bytes.buffer).setBigUint64(0, BigInt.asUintN(64, value), false) + return bytes +} + +/** + * A filter value accepted by {@link prepareSubkeyValue}. + * Solana public keys must be passed as their 32 raw bytes + * (see {@link solanaAddressToBytes}), not as base58 strings. + */ +export type SolanaSubkeyValue = Uint8Array | string | bigint | number + +/** + * Encodes a filter value for use in a `SubkeyConfig` `ValueComparator`, + * matching the byte representation the Solana log-trigger capability indexes. + * + * Encodings (mirrors Go `bindings.PrepareSubkeyValue`): + * - `Uint8Array` — used as-is (public keys, fixed byte arrays, `bytes` fields) + * - `string` — UTF-8 bytes + * - `bigint` / integer `number` — 8-byte big-endian two's complement + * + * For `f32`/`f64` event fields use {@link prepareSubkeyFloatValue}; passing a + * non-integer `number` here throws. + * + * @param value - The filter value to encode. + * @returns The encoded comparator value bytes. + */ +export const prepareSubkeyValue = (value: SolanaSubkeyValue): Uint8Array => { + if (value instanceof Uint8Array) { + return value + } + if (typeof value === 'string') { + return UTF8_ENCODER.encode(value) + } + if (typeof value === 'bigint') { + return u64BE(value) + } + if (typeof value === 'number') { + if (!Number.isSafeInteger(value)) { + throw new Error( + 'prepareSubkeyValue: non-integer number; use prepareSubkeyFloatValue for f32/f64 values', + ) + } + return u64BE(BigInt(value)) + } + throw new Error(`prepareSubkeyValue: unsupported subkey value type ${typeof value}`) +} + +/** + * Encodes an `f32`/`f64` filter value into the 8-byte big-endian + * representation the Solana log-trigger capability indexes. + * + * Byte-compatible with Go `bindings.EncodeIndexedValue` for floats: positive + * values map to `float64bits + 2^63`, others to `2^63 - float64bits` + * (modulo 2^64). Ordering is preserved among values of the same sign. + * + * @param value - The float filter value to encode. + * @returns The encoded comparator value bytes. + */ +export const prepareSubkeyFloatValue = (value: number): Uint8Array => { + const view = new DataView(new ArrayBuffer(U64_LENGTH)) + view.setFloat64(0, value, false) + const bits = view.getBigUint64(0, false) + return u64BE(value > 0 ? bits + U64_SIGN_BIT : U64_SIGN_BIT - bits) +} + +/** + * A Solana log paired with its decoded event data. Generated log-trigger + * bindings adapt raw `Log` payloads into this shape. + * + * Mirrors Go `bindings.DecodedLog[T]`. + */ +export type SolanaDecodedLog = { + log: Log + data: T +} + +/** Options for generated Solana log-trigger bindings. */ +export interface SolanaLogTriggerOptions { + /** + * Listen for events emitted via Anchor's `emit_cpi!` (self-CPI) instead of + * program logs. Sets the trigger's CPI filter to the program's + * `anchor:event` method (see {@link anchorCPILogTriggerConfig}). + */ + cpi?: boolean +} + +/** Method name Anchor uses for `emit_cpi!` self-invocations. */ +const ANCHOR_CPI_METHOD_NAME = 'anchor:event' +const ANCHOR_CPI_METHOD_NAME_B64 = bytesToBase64(UTF8_ENCODER.encode(ANCHOR_CPI_METHOD_NAME)) + +/** + * Builds the `CPIFilterConfig` for log triggers on events emitted via + * Anchor's `emit_cpi!`: destination is the program itself, method is + * `anchor:event`. + * + * Mirrors Go `bindings.AnchorCPILogTriggerConfig`. + * + * @param programId - The program's public key, as 32 raw bytes or a base58 string. + * @returns The CPI filter config in protobuf JSON shape. + */ +export const anchorCPILogTriggerConfig = (programId: Uint8Array | string): CPIFilterConfigJson => ({ + destAddress: bytesToBase64( + typeof programId === 'string' ? solanaAddressToBytes(programId) : programId, + ), + methodName: ANCHOR_CPI_METHOD_NAME_B64, +}) diff --git a/packages/cre-sdk/src/sdk/utils/index.ts b/packages/cre-sdk/src/sdk/utils/index.ts index 6b03b312..ef6a2ec5 100644 --- a/packages/cre-sdk/src/sdk/utils/index.ts +++ b/packages/cre-sdk/src/sdk/utils/index.ts @@ -7,6 +7,7 @@ export * from './chain-selectors' export * from './decode-json' export * from './hex-utils' export * from './safe-json-stringify' +export * from './triggers/trigger-interface' export * from './values/consensus_aggregators' export * from './values/serializer_types' export * from './values/value' diff --git a/packages/cre-sdk/src/sdk/utils/triggers/trigger-interface.ts b/packages/cre-sdk/src/sdk/utils/triggers/trigger-interface.ts index 4a028975..57e4319f 100644 --- a/packages/cre-sdk/src/sdk/utils/triggers/trigger-interface.ts +++ b/packages/cre-sdk/src/sdk/utils/triggers/trigger-interface.ts @@ -27,3 +27,20 @@ export interface Trigger< /** Transform the raw trigger output to the adapted type */ adapt(rawOutput: TRawTriggerOutput): TTriggerOutput } + +/** + * Wraps a trigger with an extra output transformation, delegating everything + * else to the wrapped trigger. The TypeScript analog of Go bindings embedding + * `cre.Trigger` and overriding only `Adapt` — generated bindings use this to + * decode raw trigger outputs into typed data. + */ +export const adaptTrigger = , TIn, TOut>( + trigger: Trigger, + adapt: (output: TIn) => TOut, +): Trigger => ({ + capabilityId: () => trigger.capabilityId(), + method: () => trigger.method(), + outputSchema: () => trigger.outputSchema(), + configAsAny: () => trigger.configAsAny(), + adapt: (rawOutput) => adapt(trigger.adapt(rawOutput)), +}) diff --git a/scripts/e2e/simulate-hello-world.sh b/scripts/e2e/simulate-hello-world.sh index 105accac..485d3978 100755 --- a/scripts/e2e/simulate-hello-world.sh +++ b/scripts/e2e/simulate-hello-world.sh @@ -13,7 +13,11 @@ cd "$EXAMPLES_DIR" cp -n .env.example .env 2>/dev/null || true echo "Running hello-world workflow simulation..." -cre workflow simulate ./src/workflows/hello-world > "$OUTPUT_FILE" 2>&1 +if ! cre workflow simulate ./src/workflows/hello-world > "$OUTPUT_FILE" 2>&1; then + echo "❌ ERROR: cre workflow simulate failed. Output:" + cat "$OUTPUT_FILE" + exit 1 +fi cat "$OUTPUT_FILE" # --- Validation --- diff --git a/scripts/e2e/simulate-rust-inject.sh b/scripts/e2e/simulate-rust-inject.sh index 5e486e73..35393e17 100755 --- a/scripts/e2e/simulate-rust-inject.sh +++ b/scripts/e2e/simulate-rust-inject.sh @@ -49,11 +49,15 @@ if [ ! -f "./wasm/workflow.wasm" ]; then exit 1 fi +SIM_EXIT_5A=0 cre workflow simulate . \ --non-interactive \ --trigger-index 0 \ - > "$OUTPUT_5A" 2>&1 || true + > "$OUTPUT_5A" 2>&1 || SIM_EXIT_5A=$? cat "$OUTPUT_5A" +if [ "$SIM_EXIT_5A" -ne 0 ]; then + echo "⚠️ WARNING: cre workflow simulate exited with code $SIM_EXIT_5A" +fi echo "" echo "Validating prebuilt-plugin output..." @@ -74,11 +78,15 @@ if [ ! -f "./wasm/workflow.wasm" ]; then exit 1 fi +SIM_EXIT_5B=0 cre workflow simulate . \ --non-interactive \ --trigger-index 0 \ - > "$OUTPUT_5B" 2>&1 || true + > "$OUTPUT_5B" 2>&1 || SIM_EXIT_5B=$? cat "$OUTPUT_5B" +if [ "$SIM_EXIT_5B" -ne 0 ]; then + echo "⚠️ WARNING: cre workflow simulate exited with code $SIM_EXIT_5B" +fi echo "" echo "Validating source-extensions output..."