Skip to content

Commit f0d7677

Browse files
feat/starknet-sdk-integration
1 parent bc3f700 commit f0d7677

18 files changed

Lines changed: 679 additions & 6 deletions

.changeset/hip-papayas-kiss.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@hyperlane-xyz/sdk': minor
3+
---
4+
5+
feat: Starknet SDK logic integration

typescript/sdk/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"@cosmjs/stargate": "^0.32.4",
1111
"@hyperlane-xyz/core": "7.1.4",
1212
"@hyperlane-xyz/cosmos-sdk": "12.5.0",
13+
"@hyperlane-xyz/starknet-core": "1.0.0",
1314
"@hyperlane-xyz/utils": "12.5.0",
1415
"@safe-global/api-kit": "1.3.0",
1516
"@safe-global/protocol-kit": "1.3.0",

typescript/sdk/src/app/MultiProtocolApp.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
CosmJsWasmProvider,
1818
EthersV5Provider,
1919
SolanaWeb3Provider,
20+
StarknetJsProvider,
2021
TypedProvider,
2122
} from '../providers/ProviderType.js';
2223
import { ChainMap, ChainName } from '../types.js';
@@ -104,6 +105,14 @@ export class BaseSealevelAdapter extends BaseAppAdapter {
104105
}
105106
}
106107

108+
export class BaseStarknetAdapter extends BaseAppAdapter {
109+
public readonly protocol: ProtocolType = ProtocolType.Starknet;
110+
111+
public getProvider(): StarknetJsProvider['provider'] {
112+
return this.multiProvider.getStarknetProvider(this.chainName);
113+
}
114+
}
115+
107116
/**
108117
* A version of HyperlaneApp that can support different
109118
* provider types across different protocol types.

typescript/sdk/src/consts/testChains.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,13 +143,32 @@ export const testSealevelChain: ChainMetadata = {
143143
rpcUrls: [{ http: 'http://127.0.0.1:8899' }],
144144
};
145145

146+
export const testStarknetChain: ChainMetadata = {
147+
chainId: '0x534e5f5345504f4c4941',
148+
domainId: 5854809,
149+
name: 'starknetdevnet',
150+
nativeToken: {
151+
decimals: 18,
152+
denom: '0x49D36570D4E46F48E99674BD3FCC84644DDD6B96F7C741B1562B82F9E004DC7',
153+
name: 'Ether',
154+
symbol: 'ETH',
155+
},
156+
protocol: ProtocolType.Starknet,
157+
rpcUrls: [
158+
{
159+
http: 'http://127.0.0.1:5050',
160+
},
161+
],
162+
};
163+
146164
export const multiProtocolTestChainMetadata: ChainMap<ChainMetadata> = {
147165
...testChainMetadata,
148166
testcosmos: testCosmosChain,
149167
testsealevel: testSealevelChain,
150168
testxerc20: testXERC20,
151169
testvsxerc20: testVSXERC20,
152170
testxerc20lockbox: testXERC20Lockbox,
171+
starknetdevnet: testStarknetChain,
153172
};
154173

155174
export const multiProtocolTestChains: Array<ChainName> = Object.keys(

typescript/sdk/src/core/MultiProtocolCore.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { CosmNativeCoreAdapter } from './adapters/CosmNativeCoreAdapter.js';
99
import { CosmWasmCoreAdapter } from './adapters/CosmWasmCoreAdapter.js';
1010
import { EvmCoreAdapter } from './adapters/EvmCoreAdapter.js';
1111
import { SealevelCoreAdapter } from './adapters/SealevelCoreAdapter.js';
12+
import { StarknetCoreAdapter } from './adapters/StarknetCoreAdapter.js';
1213
import { ICoreAdapter } from './adapters/types.js';
1314
import { CoreAddresses } from './contracts.js';
1415

@@ -41,6 +42,7 @@ export class MultiProtocolCore extends MultiProtocolApp<
4142
if (protocol === ProtocolType.Sealevel) return SealevelCoreAdapter;
4243
if (protocol === ProtocolType.Cosmos) return CosmWasmCoreAdapter;
4344
if (protocol === ProtocolType.CosmosNative) return CosmNativeCoreAdapter;
45+
if (protocol === ProtocolType.Starknet) return StarknetCoreAdapter;
4446
throw new Error(`No adapter for protocol ${protocol}`);
4547
}
4648

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import {
2+
CallData,
3+
InvokeTransactionReceiptResponse,
4+
ParsedEvents,
5+
events as eventsUtils,
6+
} from 'starknet';
7+
8+
import { getCompiledContract } from '@hyperlane-xyz/starknet-core';
9+
import { Address, HexString, pollAsync } from '@hyperlane-xyz/utils';
10+
11+
import { BaseStarknetAdapter } from '../../app/MultiProtocolApp.js';
12+
import { MultiProtocolProvider } from '../../providers/MultiProtocolProvider.js';
13+
import {
14+
ProviderType,
15+
StarknetJsTransactionReceipt,
16+
} from '../../providers/ProviderType.js';
17+
import { ChainName } from '../../types.js';
18+
import {
19+
getStarknetMailboxContract,
20+
parseStarknetDispatchEvents,
21+
} from '../../utils/starknet.js';
22+
23+
import { ICoreAdapter } from './types.js';
24+
25+
export class StarknetCoreAdapter
26+
extends BaseStarknetAdapter
27+
implements ICoreAdapter
28+
{
29+
constructor(
30+
public readonly chainName: ChainName,
31+
public readonly multiProvider: MultiProtocolProvider,
32+
public readonly addresses: { mailbox: Address },
33+
) {
34+
super(chainName, multiProvider, addresses);
35+
}
36+
37+
extractMessageIds(
38+
sourceTx: StarknetJsTransactionReceipt,
39+
): Array<{ messageId: string; destination: ChainName }> {
40+
if (sourceTx.type !== ProviderType.Starknet) {
41+
throw new Error(
42+
`Unsupported provider type for StarknetCoreAdapter ${sourceTx.type}`,
43+
);
44+
}
45+
46+
let parsedEvents: ParsedEvents = [];
47+
sourceTx.receipt.match({
48+
success: (txR) => {
49+
const emittedEvents =
50+
(txR as InvokeTransactionReceiptResponse).events?.map((event) => {
51+
return {
52+
block_hash: (txR as any).block_hash,
53+
block_number: (txR as any).block_number,
54+
transaction_hash: (txR as any).transaction_hash,
55+
...event,
56+
};
57+
}) || [];
58+
59+
if (emittedEvents.length === 0) return;
60+
const mailboxAbi = getCompiledContract('mailbox').abi;
61+
parsedEvents = eventsUtils.parseEvents(
62+
emittedEvents,
63+
eventsUtils.getAbiEvents(mailboxAbi),
64+
CallData.getAbiStruct(mailboxAbi),
65+
CallData.getAbiEnum(mailboxAbi),
66+
);
67+
},
68+
_: () => {
69+
throw Error('This transaction was not successful.');
70+
},
71+
});
72+
73+
if (!parsedEvents || parsedEvents.length === 0) return [];
74+
75+
const messages = parseStarknetDispatchEvents(
76+
parsedEvents,
77+
(domain) => this.multiProvider.tryGetChainName(domain) ?? undefined,
78+
);
79+
80+
return messages.map(({ id, parsed }) => ({
81+
messageId: id,
82+
destination: this.multiProvider.getChainName(parsed.destination),
83+
}));
84+
}
85+
86+
async waitForMessageProcessed(
87+
messageId: HexString,
88+
destination: ChainName,
89+
delayMs = 5000,
90+
maxAttempts = 60,
91+
): Promise<boolean> {
92+
const destAdapter = new StarknetCoreAdapter(
93+
destination,
94+
this.multiProvider,
95+
{ mailbox: this.addresses.mailbox },
96+
);
97+
98+
const mailboxContract = getStarknetMailboxContract(
99+
destAdapter.addresses.mailbox,
100+
destAdapter.getProvider(),
101+
);
102+
103+
await pollAsync(
104+
async () => {
105+
const isDelivered = await mailboxContract.call('delivered', [
106+
messageId,
107+
]);
108+
109+
if (!isDelivered) {
110+
throw new Error(
111+
`Message ${messageId} not yet delivered on ${destination}`,
112+
);
113+
}
114+
115+
this.logger.debug(
116+
`Message ${messageId} confirmed delivered on ${destination}`,
117+
);
118+
119+
return isDelivered;
120+
},
121+
delayMs,
122+
maxAttempts,
123+
);
124+
125+
return true;
126+
}
127+
}

typescript/sdk/src/index.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export {
6060
export { CosmWasmCoreAdapter } from './core/adapters/CosmWasmCoreAdapter.js';
6161
export { EvmCoreAdapter } from './core/adapters/EvmCoreAdapter.js';
6262
export { SealevelCoreAdapter } from './core/adapters/SealevelCoreAdapter.js';
63+
export { StarknetCoreAdapter } from './core/adapters/StarknetCoreAdapter.js';
6364
export { ICoreAdapter } from './core/adapters/types.js';
6465
export {
6566
CoreAddresses,
@@ -365,6 +366,10 @@ export {
365366
SolanaWeb3Provider,
366367
SolanaWeb3Transaction,
367368
SolanaWeb3TransactionReceipt,
369+
StarknetJsContract,
370+
StarknetJsProvider,
371+
StarknetJsTransaction,
372+
StarknetJsTransactionReceipt,
368373
TypedContract,
369374
TypedProvider,
370375
TypedTransaction,
@@ -705,3 +710,12 @@ export {
705710
CCIPContractCache,
706711
} from './ccip/utils.js';
707712
export { HyperlaneCCIPDeployer } from './ccip/HyperlaneCCIPDeployer.js';
713+
714+
export {
715+
StarknetContractName,
716+
getStarknetContract,
717+
getStarknetHypERC20Contract,
718+
getStarknetHypERC20CollateralContract,
719+
getStarknetMailboxContract,
720+
getStarknetEtherContract,
721+
} from './utils/starknet.js';

typescript/sdk/src/providers/MultiProtocolProvider.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
ProviderMap,
2626
ProviderType,
2727
SolanaWeb3Provider,
28+
StarknetJsProvider,
2829
TypedProvider,
2930
TypedTransaction,
3031
ViemProvider,
@@ -215,6 +216,15 @@ export class MultiProtocolProvider<
215216
);
216217
}
217218

219+
getStarknetProvider(
220+
chainNameOrId: ChainNameOrId,
221+
): StarknetJsProvider['provider'] {
222+
return this.getSpecificProvider<StarknetJsProvider['provider']>(
223+
chainNameOrId,
224+
ProviderType.Starknet,
225+
);
226+
}
227+
218228
setProvider(
219229
chainNameOrId: ChainNameOrId,
220230
provider: TypedProvider,

typescript/sdk/src/providers/MultiProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ export class MultiProvider<MetaExt = {}> extends ChainMetadataManager<MetaExt> {
316316
// setup contract factory
317317
const overrides = this.getTransactionOverrides(chainNameOrId);
318318
const signer = this.getSigner(chainNameOrId);
319-
const contractFactory = await factory.connect(signer);
319+
const contractFactory = factory.connect(signer);
320320

321321
// estimate gas
322322
const deployTx = contractFactory.getDeployTransaction(...params);

typescript/sdk/src/providers/ProviderType.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ import {
1919
Contract as StarknetContract,
2020
Invocation as StarknetInvocation,
2121
Provider as StarknetProvider,
22-
ReceiptTx as StarknetReceiptTx,
23-
TransactionReceipt as StarknetTxReceipt,
22+
GetTransactionReceiptResponse as StarknetTxReceipt,
2423
} from 'starknet';
2524
import type {
2625
GetContractReturnType,
@@ -356,9 +355,9 @@ export interface CosmJsNativeTransactionReceipt
356355
}
357356

358357
export interface StarknetJsTransactionReceipt
359-
extends TypedTransactionReceiptBase<StarknetTxReceipt | StarknetReceiptTx> {
358+
extends TypedTransactionReceiptBase<StarknetTxReceipt> {
360359
type: ProviderType.Starknet;
361-
receipt: StarknetTxReceipt | StarknetReceiptTx;
360+
receipt: StarknetTxReceipt;
362361
}
363362

364363
export interface ZKSyncTransactionReceipt

0 commit comments

Comments
 (0)