Skip to content

Commit 9997e60

Browse files
Update gas station SDK to reflect audited contract changes, including new contract addresses and EIP-712 field name adjustments. Modify related documentation and examples for consistency.
1 parent 632d53c commit 9997e60

File tree

3 files changed

+75
-57
lines changed

3 files changed

+75
-57
lines changed

.changeset/chubby-peas-sip.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,21 @@
22
"@turnkey/gas-station": major
33
---
44

5-
Update gas station contract addresses and modify intent builder types for post-audit contract
5+
Updated the `@turnkey/gas-station` SDK to align with the audited smart contract changes. The audit resulted in several interface updates:
6+
7+
**Contract Changes:**
8+
- **New contract addresses**: Updated both delegate and execution contract addresses to the newly deployed versions
9+
- **EIP-712 field name changes**: The canonical delegate contract interface uses simplified field names (`to`, `value`, `data`) instead of the previous descriptive names (`outputContract`, `ethAmount`, `arguments`)
10+
11+
**SDK Updates:**
12+
- Updated `DEFAULT_EXECUTION_CONTRACT` address from `0x4ece92b06C7d2d99d87f052E0Fca47Fb180c3348` to `0x00000000008c57a1CE37836a5e9d36759D070d8c`
13+
- Updated `DEFAULT_DELEGATE_CONTRACT` address from `0xC2a37Ee08cAc3778d9d05FF0a93FD5B553C77E3a` to `0x000066a00056CD44008768E2aF00696e19A30084`
14+
- Updated EIP-712 Execution typehash field names to match the contract's canonical interface
15+
- Updated EIP-712 ApproveThenExecute typehash field names to match the contract's canonical interface
16+
- Updated Turnkey policy conditions in `buildIntentSigningPolicy` to reference the new field names (`to`, `value` instead of `outputContract`, `ethAmount`)
17+
- Updated documentation and examples to reflect the new field names
18+
19+
**Files Modified:**
20+
- `packages/gas-station/src/config.ts` - Updated contract addresses
21+
- `packages/gas-station/src/intentBuilder.ts` - Updated EIP-712 type definitions and message objects
22+
- `packages/gas-station/src/policyUtils.ts` - Updated policy condition field references and documentation

packages/gas-station/README.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -343,8 +343,8 @@ import { buildIntentSigningPolicy } from "@turnkey/gas-station";
343343

344344
// USDC-only policy
345345
const eoaPolicy = buildIntentSigningPolicy({
346-
organizationId: "your-org-id",
347-
eoaUserId: "user-id",
346+
organizationId: "a5b89e4f-1234-5678-9abc-def012345678",
347+
eoaUserId: "3c7d6e8a-4b5c-6d7e-8f9a-0b1c2d3e4f5a",
348348
restrictions: {
349349
allowedContracts: ["0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"], // USDC on Base
350350
disallowEthTransfer: true, // Disallow ETH transfers
@@ -354,10 +354,10 @@ const eoaPolicy = buildIntentSigningPolicy({
354354

355355
// Resulting policy restricts signing to USDC transfers only:
356356
// {
357-
// organizationId: "your-org-id",
357+
// organizationId: "a5b89e4f-1234-5678-9abc-def012345678",
358358
// policyName: "USDC Only Policy",
359359
// effect: "EFFECT_ALLOW",
360-
// consensus: "approvers.any(user, user.id == 'user-id')",
360+
// consensus: "approvers.any(user, user.id == '3c7d6e8a-4b5c-6d7e-8f9a-0b1c2d3e4f5a')",
361361
// condition: "activity.resource == 'PRIVATE_KEY' && " +
362362
// "activity.action == 'SIGN' && " +
363363
// "eth.eip_712.primary_type == 'Execution' && " +
@@ -390,8 +390,8 @@ await ensureGasStationInterface(
390390

391391
// Paymaster protection policy with ETH amount limit
392392
const paymasterPolicy = buildPaymasterExecutionPolicy({
393-
organizationId: "paymaster-org-id",
394-
paymasterUserId: "paymaster-user-id",
393+
organizationId: "f8c3a5e7-9876-5432-1abc-def098765432",
394+
paymasterUserId: "8f2a1b4c-5d6e-7f8a-9b0c-1d2e3f4a5b6c",
395395
executionContractAddress: DEFAULT_EXECUTION_CONTRACT,
396396
restrictions: {
397397
allowedEOAs: ["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"],
@@ -405,23 +405,23 @@ const paymasterPolicy = buildPaymasterExecutionPolicy({
405405

406406
// Resulting policy uses ABI parsing for direct argument access:
407407
// {
408-
// organizationId: "paymaster-org-id",
408+
// organizationId: "f8c3a5e7-9876-5432-1abc-def098765432",
409409
// policyName: "Paymaster Protection",
410410
// effect: "EFFECT_ALLOW",
411-
// consensus: "approvers.any(user, user.id == 'paymaster-user-id')",
411+
// consensus: "approvers.any(user, user.id == '8f2a1b4c-5d6e-7f8a-9b0c-1d2e3f4a5b6c')",
412412
// condition: "activity.resource == 'PRIVATE_KEY' && " +
413413
// "activity.action == 'SIGN' && " +
414-
// "eth.tx.to == '0xe511ad0a281c10b8408381e2ab8525abe587827b' && " +
414+
// "eth.tx.to == '0x00000000008c57a1ce37836a5e9d36759d070d8c' && " +
415415
// "(eth.tx.contract_call_args['_to'] == '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913') && " +
416-
// "(eth.tx.contract_call_args['_targetEoA'] == '0x742d35cc6634c0532925a3b844bc9e7595f0beb') && " +
417-
// "eth.tx.contract_call_args['ethAmount'] <= 100000000000000000 && " +
416+
// "(eth.tx.contract_call_args['_target'] == '0x742d35cc6634c0532925a3b844bc9e7595f0beb') && " +
417+
// "eth.tx.contract_call_args['_ethAmount'] <= 100000000000000000 && " +
418418
// "eth.tx.gasPrice <= 50000000000 && " +
419419
// "eth.tx.gas <= 500000",
420420
// notes: "Restricts which transactions the paymaster can execute on the gas station"
421421
// }
422422
```
423423

424-
**Note:** The `ensureGasStationInterface()` function uploads the Gas Station ABI to Turnkey's Smart Contract Interface feature. This enables Turnkey's policy engine to parse the ABI-encoded transaction data and directly compare the `ethAmount` parameter as a uint256 value, rather than raw bytes. The function checks if the ABI already exists before uploading to avoid duplicates.
424+
**Note:** The `ensureGasStationInterface()` function uploads the Gas Station ABI to Turnkey's Smart Contract Interface feature. This enables Turnkey's policy engine to parse the ABI-encoded transaction data and directly compare the `_ethAmount` parameter as a uint256 value, rather than raw bytes. The function checks if the ABI already exists before uploading to avoid duplicates.
425425

426426
#### Defense in Depth
427427

@@ -437,8 +437,8 @@ import { parseGwei } from "viem";
437437

438438
// Layer 1: EOA can only sign USDC intents
439439
const eoaPolicy = buildIntentSigningPolicy({
440-
organizationId: "user-org",
441-
eoaUserId: "user-id",
440+
organizationId: "a5b89e4f-1234-5678-9abc-def012345678",
441+
eoaUserId: "3c7d6e8a-4b5c-6d7e-8f9a-0b1c2d3e4f5a",
442442
restrictions: {
443443
allowedContracts: ["0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"],
444444
disallowEthTransfer: true, // No ETH transfers
@@ -447,8 +447,8 @@ const eoaPolicy = buildIntentSigningPolicy({
447447

448448
// Layer 2: Paymaster can only execute for specific users with gas limits
449449
const paymasterPolicy = buildPaymasterExecutionPolicy({
450-
organizationId: "paymaster-org",
451-
paymasterUserId: "paymaster-user-id",
450+
organizationId: "f8c3a5e7-9876-5432-1abc-def098765432",
451+
paymasterUserId: "8f2a1b4c-5d6e-7f8a-9b0c-1d2e3f4a5b6c",
452452
executionContractAddress: DEFAULT_EXECUTION_CONTRACT,
453453
restrictions: {
454454
allowedEOAs: ["0xUserAddress..."],
@@ -528,7 +528,7 @@ Allow paymaster to execute for USDC or DAI only:
528528

529529
```typescript
530530
const policy = {
531-
organizationId: "paymaster-org-id",
531+
organizationId: "f8c3a5e7-9876-5432-1abc-def098765432",
532532
policyName: "Stablecoin Execution Policy",
533533
effect: "EFFECT_ALLOW",
534534
consensus: `approvers.any(user, user.id == '${paymasterUserId}')`,
@@ -566,7 +566,7 @@ const eoaConditions = approvedEOAs
566566
.join(" || ");
567567

568568
const policy = {
569-
organizationId: "paymaster-org-id",
569+
organizationId: "f8c3a5e7-9876-5432-1abc-def098765432",
570570
policyName: "Approved Users Only",
571571
effect: "EFFECT_ALLOW",
572572
consensus: `approvers.any(user, user.id == '${paymasterUserId}')`,

packages/gas-station/src/policyUtils.ts

Lines changed: 39 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ import type { Hex } from "viem";
2222
* @example
2323
* // Simple: single user approval, token-only transfers
2424
* const policy = buildIntentSigningPolicy({
25-
* organizationId: "org-123",
26-
* eoaUserId: "user-456",
25+
* organizationId: "a5b89e4f-1234-5678-9abc-def012345678",
26+
* eoaUserId: "3c7d6e8a-4b5c-6d7e-8f9a-0b1c2d3e4f5a",
2727
* restrictions: {
2828
* allowedContracts: ["0x833...USDC", "0x6B1...DAI"],
2929
* disallowEthTransfer: true,
@@ -33,10 +33,10 @@ import type { Hex } from "viem";
3333
*
3434
* // Resulting policy:
3535
* {
36-
* organizationId: "org-123",
36+
* organizationId: "a5b89e4f-1234-5678-9abc-def012345678",
3737
* policyName: "Stablecoin Only",
3838
* effect: "EFFECT_ALLOW",
39-
* consensus: "approvers.any(user, user.id == 'user-456')",
39+
* consensus: "approvers.any(user, user.id == '3c7d6e8a-4b5c-6d7e-8f9a-0b1c2d3e4f5a')",
4040
* condition: "activity.resource == 'PRIVATE_KEY' && activity.action == 'SIGN' && " +
4141
* "eth.eip_712.primary_type == 'Execution' && " +
4242
* "(eth.eip_712.message['to'] == '0x833...usdc' || " +
@@ -48,9 +48,9 @@ import type { Hex } from "viem";
4848
* @example
4949
* // Multi-approval: EOA AND backup user must both approve
5050
* const policy = buildIntentSigningPolicy({
51-
* organizationId: "org-123",
52-
* eoaUserId: "user-456",
53-
* additionalApprovers: ["backup-user-789"],
51+
* organizationId: "a5b89e4f-1234-5678-9abc-def012345678",
52+
* eoaUserId: "3c7d6e8a-4b5c-6d7e-8f9a-0b1c2d3e4f5a",
53+
* additionalApprovers: ["9d4e2f5b-6c7d-8e9f-0a1b-2c3d4e5f6a7b"],
5454
* restrictions: {
5555
* allowedContracts: ["0x833...USDC"],
5656
* disallowEthTransfer: true,
@@ -59,11 +59,11 @@ import type { Hex } from "viem";
5959
*
6060
* // Resulting policy:
6161
* {
62-
* organizationId: "org-123",
62+
* organizationId: "a5b89e4f-1234-5678-9abc-def012345678",
6363
* policyName: "Gas Station Intent Signing Policy",
6464
* effect: "EFFECT_ALLOW",
65-
* consensus: "approvers.any(user, user.id == 'user-456') && " +
66-
* "approvers.any(user, user.id == 'backup-user-789')",
65+
* consensus: "approvers.any(user, user.id == '3c7d6e8a-4b5c-6d7e-8f9a-0b1c2d3e4f5a') && " +
66+
* "approvers.any(user, user.id == '9d4e2f5b-6c7d-8e9f-0a1b-2c3d4e5f6a7b')",
6767
* condition: "activity.resource == 'PRIVATE_KEY' && activity.action == 'SIGN' && " +
6868
* "eth.eip_712.primary_type == 'Execution' && " +
6969
* "(eth.eip_712.message['to'] == '0x833...usdc') && " +
@@ -74,8 +74,8 @@ import type { Hex } from "viem";
7474
* @example
7575
* // Advanced: custom consensus expression
7676
* const policy = buildIntentSigningPolicy({
77-
* organizationId: "org-123",
78-
* eoaUserId: "user-456",
77+
* organizationId: "a5b89e4f-1234-5678-9abc-def012345678",
78+
* eoaUserId: "3c7d6e8a-4b5c-6d7e-8f9a-0b1c2d3e4f5a",
7979
* customConsensus: "approvers.count() >= 2",
8080
* restrictions: {
8181
* disallowEthTransfer: false,
@@ -108,7 +108,9 @@ export function buildIntentSigningPolicy(config: {
108108
// Build OR conditions for each allowed contract
109109
// Convert to lowercase for case-insensitive comparison
110110
const contractConditions = config.restrictions.allowedContracts
111-
.map((c) => `eth.eip_712.message['to'] == '${c.toLowerCase()}'`)
111+
.map(
112+
(c) => `eth.eip_712.message['to'] == '${c.toLowerCase()}'`,
113+
)
112114
.join(" || ");
113115
conditions.push(`(${contractConditions})`);
114116
}
@@ -177,8 +179,8 @@ export function buildIntentSigningPolicy(config: {
177179
* @example
178180
* // Simple: single paymaster approval with ETH amount limit
179181
* const policy = buildPaymasterExecutionPolicy({
180-
* organizationId: "org-paymaster",
181-
* paymasterUserId: "paymaster-user-123",
182+
* organizationId: "f8c3a5e7-9876-5432-1abc-def098765432",
183+
* paymasterUserId: "8f2a1b4c-5d6e-7f8a-9b0c-1d2e3f4a5b6c",
182184
* executionContractAddress: "0x00000000008c57a1CE37836a5e9d36759D070d8c",
183185
* restrictions: {
184186
* allowedEOAs: ["0xAli...ce", "0xBob...by"],
@@ -192,27 +194,27 @@ export function buildIntentSigningPolicy(config: {
192194
*
193195
* // Resulting policy (uses ABI parsing):
194196
* {
195-
* organizationId: "org-paymaster",
197+
* organizationId: "f8c3a5e7-9876-5432-1abc-def098765432",
196198
* policyName: "Paymaster Protection",
197199
* effect: "EFFECT_ALLOW",
198-
* consensus: "approvers.any(user, user.id == 'paymaster-user-123')",
200+
* consensus: "approvers.any(user, user.id == '8f2a1b4c-5d6e-7f8a-9b0c-1d2e3f4a5b6c')",
199201
* condition: "activity.resource == 'PRIVATE_KEY' && activity.action == 'SIGN' && " +
200202
* "eth.tx.to == '0x00000000008c57a1ce37836a5e9d36759d070d8c' && " +
201203
* "(eth.tx.contract_call_args['_to'] == '0x833...usdc' || " +
202204
* "eth.tx.contract_call_args['_to'] == '0x6b1...dai') && " +
203-
* "(eth.tx.contract_call_args['_targetEoA'] == '0xali...ce' || " +
204-
* "eth.tx.contract_call_args['_targetEoA'] == '0xbob...by') && " +
205-
* "eth.tx.contract_call_args['ethAmount'] <= 100000000000000000 && " +
205+
* "(eth.tx.contract_call_args['_target'] == '0xali...ce' || " +
206+
* "eth.tx.contract_call_args['_target'] == '0xbob...by') && " +
207+
* "eth.tx.contract_call_args['_ethAmount'] <= 100000000000000000 && " +
206208
* "eth.tx.gasPrice <= 50000000000 && eth.tx.gas <= 500000",
207209
* notes: "Restricts which execute() transactions the paymaster can submit on-chain"
208210
* }
209211
*
210212
* @example
211213
* // Multi-user: primary paymaster OR backup can approve
212214
* const policy = buildPaymasterExecutionPolicy({
213-
* organizationId: "org-paymaster",
214-
* paymasterUserId: "paymaster-user-123",
215-
* additionalApprovers: ["backup-paymaster-456"],
215+
* organizationId: "f8c3a5e7-9876-5432-1abc-def098765432",
216+
* paymasterUserId: "8f2a1b4c-5d6e-7f8a-9b0c-1d2e3f4a5b6c",
217+
* additionalApprovers: ["2e4f6a8b-9c0d-1e2f-3a4b-5c6d7e8f9a0b"],
216218
* executionContractAddress: "0x00000000008c57a1CE37836a5e9d36759D070d8c",
217219
* restrictions: {
218220
* allowedEOAs: ["0xAli...ce"],
@@ -223,24 +225,24 @@ export function buildIntentSigningPolicy(config: {
223225
*
224226
* // Resulting policy (uses ABI parsing):
225227
* {
226-
* organizationId: "org-paymaster",
228+
* organizationId: "f8c3a5e7-9876-5432-1abc-def098765432",
227229
* policyName: "Gas Station Paymaster Execution Policy",
228230
* effect: "EFFECT_ALLOW",
229-
* consensus: "approvers.any(user, user.id == 'paymaster-user-123' || " +
230-
* "user.id == 'backup-paymaster-456')",
231+
* consensus: "approvers.any(user, user.id == '8f2a1b4c-5d6e-7f8a-9b0c-1d2e3f4a5b6c' || " +
232+
* "user.id == '2e4f6a8b-9c0d-1e2f-3a4b-5c6d7e8f9a0b')",
231233
* condition: "activity.resource == 'PRIVATE_KEY' && activity.action == 'SIGN' && " +
232234
* "eth.tx.to == '0x00000000008c57a1ce37836a5e9d36759d070d8c' && " +
233-
* "(eth.tx.contract_call_args['_targetEoA'] == '0xali...ce') && " +
234-
* "eth.tx.contract_call_args['ethAmount'] <= 1000000000000000000 && " +
235+
* "(eth.tx.contract_call_args['_target'] == '0xali...ce') && " +
236+
* "eth.tx.contract_call_args['_ethAmount'] <= 1000000000000000000 && " +
235237
* "eth.tx.gasPrice <= 100000000000",
236238
* notes: "Restricts which execute() transactions the paymaster can submit on-chain"
237239
* }
238240
*
239241
* @example
240242
* // Advanced: require multiple approvals
241243
* const policy = buildPaymasterExecutionPolicy({
242-
* organizationId: "org-paymaster",
243-
* paymasterUserId: "paymaster-user-123",
244+
* organizationId: "f8c3a5e7-9876-5432-1abc-def098765432",
245+
* paymasterUserId: "8f2a1b4c-5d6e-7f8a-9b0c-1d2e3f4a5b6c",
244246
* customConsensus: "approvers.count() >= 2",
245247
* executionContractAddress: "0x00000000008c57a1CE37836a5e9d36759D070d8c",
246248
* restrictions: { ... },
@@ -270,7 +272,7 @@ export function buildPaymasterExecutionPolicy(config: {
270272
];
271273

272274
// Check output contract address using ABI parsing
273-
// Turnkey parses execute(address _targetEoA, address _to, uint256 ethAmount, bytes _data)
275+
// Turnkey parses execute(address _target, address _to, uint256 _ethAmount, bytes _data)
274276
// and exposes arguments via eth.tx.contract_call_args
275277
if (
276278
config.restrictions?.allowedContracts &&
@@ -440,13 +442,12 @@ export async function uploadGasStationInterface({
440442
* @returns The smart contract interface ID
441443
*
442444
* @example
443-
* const interfaceId = await ensureGasStationInterface(
444-
* turnkeyClient.apiClient(),
445-
* "org-123",
446-
* "0x576A4D741b96996cc93B4919a04c16545734481f",
447-
* undefined,
448-
* "Base Sepolia"
449-
* );
445+
* const interfaceId = await ensureGasStationInterface({
446+
* client: turnkeyClient.apiClient(),
447+
* organizationId: "a5b89e4f-1234-5678-9abc-def012345678",
448+
* contractAddress: "0x00000000008c57a1CE37836a5e9d36759D070d8c",
449+
* chainName: "Base Sepolia"
450+
* });
450451
*/
451452
export async function ensureGasStationInterface({
452453
client,

0 commit comments

Comments
 (0)