Skip to content

Commit b45fb51

Browse files
authored
Sui Integration Test (#79)
1 parent 886a14e commit b45fb51

File tree

11 files changed

+175
-48
lines changed

11 files changed

+175
-48
lines changed

frontend/claim_sdk/claim.ts

+8
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@ export class ClaimInfo {
7373
}
7474
break
7575
}
76+
case 'sui': {
77+
identityStruct = {
78+
sui: {
79+
address: Buffer.from(removeLeading0x(this.identity), 'hex'),
80+
},
81+
}
82+
break
83+
}
7684
default: {
7785
// TODO: support the other ecosystems
7886
throw new Error(`unknown ecosystem type: ${this.ecosystem}`)

frontend/claim_sdk/ecosystems/signatures.test.ts

+31-1
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@ import {
1515
TestAptosWallet,
1616
TestCosmWasmWallet,
1717
TestEvmWallet,
18+
TestSuiWallet,
1819
} from '../testWallets'
1920
import path from 'path'
2021
import { Address as InjectiveAddress } from '@injectivelabs/sdk-ts'
2122
import { airdrop } from '../solana'
2223
import { ed25519 } from '@noble/curves/ed25519'
24+
import { Ed25519PublicKey } from '@mysten/sui.js/keypairs/ed25519'
25+
import { blake2b } from '@noble/hashes/blake2b'
2326

2427
describe('signature tests', () => {
2528
const solanaKeypair = anchor.web3.Keypair.generate()
@@ -116,7 +119,7 @@ describe('signature tests', () => {
116119

117120
test('Aptos signature', async () => {
118121
const aptosPrivateKeyPath = path.resolve(
119-
await __dirname,
122+
__dirname,
120123
'../../integration/keys/aptos_private_key.json'
121124
)
122125

@@ -141,4 +144,31 @@ describe('signature tests', () => {
141144

142145
await provider.sendAndConfirm(txn, [solanaKeypair])
143146
}, 40000)
147+
148+
test('Sui signature', async () => {
149+
const suiPrivateKeyPath = path.resolve(
150+
__dirname,
151+
'../../integration/keys/sui_private_key.json'
152+
)
153+
const testWallet = TestSuiWallet.fromKeyfile(suiPrivateKeyPath)
154+
const payload = 'Test payload'
155+
const signedMessage = await testWallet.signMessage(payload)
156+
const pubkey = new Ed25519PublicKey(signedMessage.publicKey)
157+
158+
const verified = await pubkey.verify(
159+
signedMessage.fullMessage,
160+
signedMessage.signature
161+
)
162+
expect(verified).toBeTruthy()
163+
let ix = Ed25519Program.createInstructionWithPublicKey({
164+
publicKey: signedMessage.publicKey,
165+
message: signedMessage.fullMessage,
166+
signature: signedMessage.signature,
167+
})
168+
169+
const txn = new Transaction()
170+
txn.add(ix)
171+
172+
await provider.sendAndConfirm(txn, [solanaKeypair])
173+
})
144174
})

frontend/claim_sdk/ecosystems/signatures.ts

+17-1
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ import {
1212
getUncompressedPubkey,
1313
} from './cosmos'
1414
import { Hash } from '@keplr-wallet/crypto'
15-
import { HexString } from 'aptos'
1615
import { aptosGetFullMessage } from './aptos'
16+
import { splitSignatureAndPubkey, suiGetFullMessage } from './sui'
17+
import { blake2b } from '@noble/hashes/blake2b'
1718

1819
export type SignedMessage = {
1920
publicKey: Uint8Array
@@ -87,3 +88,18 @@ export function aptosBuildSignedMessage(
8788
fullMessage: Buffer.from(aptosGetFullMessage(payload), 'utf-8'),
8889
}
8990
}
91+
92+
export function suiBuildSignedMessage(
93+
response: string,
94+
payload: string
95+
): SignedMessage {
96+
const [signature, publicKey] = splitSignatureAndPubkey(
97+
Buffer.from(response, 'base64')
98+
)
99+
return {
100+
publicKey,
101+
signature,
102+
recoveryId: undefined,
103+
fullMessage: blake2b(suiGetFullMessage(payload), { dkLen: 32 }),
104+
}
105+
}

frontend/claim_sdk/solana.ts

+7-18
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { ClaimInfo, Ecosystem } from './claim'
1818
import { TOKEN_PROGRAM_ID, Token } from '@solana/spl-token'
1919
import { SignedMessage } from './ecosystems/signatures'
2020
import { extractChainId } from './ecosystems/cosmos'
21+
import { blake2b } from '@noble/hashes/blake2b'
2122

2223
type bump = number
2324
// NOTE: This must be kept in sync with the on-chain program
@@ -265,9 +266,12 @@ export class TokenDispenserProvider {
265266

266267
if (signedMessage) {
267268
switch (claimInfo.ecosystem) {
268-
case 'evm': {
269+
case 'evm':
270+
case 'aptos':
271+
case 'sui':
272+
case 'injective': {
269273
return {
270-
evm: {
274+
[claimInfo.ecosystem]: {
271275
pubkey: Array.from(signedMessage.publicKey),
272276
verificationInstructionIndex: 0,
273277
},
@@ -284,14 +288,6 @@ export class TokenDispenserProvider {
284288
},
285289
}
286290
}
287-
case 'injective': {
288-
return {
289-
injective: {
290-
pubkey: Array.from(signedMessage.publicKey), //evm Pubkey
291-
verificationInstructionIndex: 0,
292-
},
293-
}
294-
}
295291
case 'discord': {
296292
return {
297293
discord: {
@@ -300,14 +296,6 @@ export class TokenDispenserProvider {
300296
},
301297
}
302298
}
303-
case 'aptos': {
304-
return {
305-
aptos: {
306-
pubkey: Array.from(signedMessage.publicKey), //ed25519 pubkey
307-
verificationInstructionIndex: 0,
308-
},
309-
}
310-
}
311299
//TODO: implement other ecosystems
312300
default: {
313301
throw new Error(`unknown ecosystem type: ${claimInfo.ecosystem}`)
@@ -343,6 +331,7 @@ export class TokenDispenserProvider {
343331
return undefined
344332
}
345333
case 'discord':
334+
case 'sui':
346335
case 'aptos': {
347336
return Ed25519Program.createInstructionWithPublicKey({
348337
publicKey: signedMessage.publicKey,

frontend/claim_sdk/testWallets.ts

+41-18
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
cosmwasmBuildSignedMessage,
66
evmBuildSignedMessage,
77
aptosBuildSignedMessage,
8+
suiBuildSignedMessage,
89
} from './ecosystems/signatures'
910
import { ethers } from 'ethers'
1011
import fs from 'fs'
@@ -15,8 +16,9 @@ import { Keypair, PublicKey } from '@solana/web3.js'
1516
import { EthSecp256k1Wallet } from '@injectivelabs/sdk-ts/dist/cjs/core/accounts/signers/EthSecp256k1Wallet'
1617
import { OfflineAminoSigner } from '@injectivelabs/sdk-ts/dist/cjs/core/accounts/signers/types/amino-signer'
1718
import { hardDriveSignMessage, signDiscordMessage } from './ecosystems/solana'
18-
import { AptosAccount, HexString } from 'aptos'
19+
import { AptosAccount } from 'aptos'
1920
import { aptosGetFullMessage } from './ecosystems/aptos'
21+
import { Ed25519Keypair } from '@mysten/sui.js/keypairs/ed25519'
2022

2123
const KEY_DIR = './integration/keys/'
2224
export const TEST_DISCORD_USERNAME =
@@ -39,42 +41,42 @@ export function loadAnchorWallet(): NodeWallet {
3941
export async function loadTestWallets(): Promise<
4042
Record<Ecosystem, TestWallet[]>
4143
> {
42-
const evmWallet = TestEvmWallet.fromKeyfile(
43-
path.resolve(KEY_DIR, 'evm_private_key.json')
44-
)
44+
const evmPrivateKeyPath = path.resolve(KEY_DIR, 'evm_private_key.json')
4545
const cosmosPrivateKeyPath = path.resolve(KEY_DIR, 'cosmos_private_key.json')
4646
const aptosPrivateKeyPath = path.resolve(KEY_DIR, 'aptos_private_key.json')
47+
const suiPrivateKeyPath = path.resolve(KEY_DIR, 'sui_private_key.json')
48+
4749
const dispenserGuardKeyPath = path.resolve(
4850
KEY_DIR,
4951
'dispenser_guard_private_key.json'
5052
)
5153
const solanaPrivateKeyPath = path.resolve(KEY_DIR, 'solana_private_key.json')
5254

5355
const result: Record<Ecosystem, TestWallet[]> = {
54-
evm: [],
55-
cosmwasm: [],
56+
discord: [],
5657
solana: [],
57-
aptos: [],
58+
evm: [],
5859
sui: [],
59-
discord: [],
60+
aptos: [],
61+
cosmwasm: [],
6062
injective: [],
6163
}
62-
result['evm'] = [evmWallet]
63-
64+
result['discord'] = [
65+
DiscordTestWallet.fromKeyfile(TEST_DISCORD_USERNAME, dispenserGuardKeyPath),
66+
]
67+
result['solana'] = [TestSolanaWallet.fromKeyfile(solanaPrivateKeyPath)]
68+
result['evm'] = [TestEvmWallet.fromKeyfile(evmPrivateKeyPath)]
69+
result['sui'] = [TestSuiWallet.fromKeyfile(suiPrivateKeyPath)]
70+
result['aptos'] = [TestAptosWallet.fromKeyfile(aptosPrivateKeyPath)]
6471
result['cosmwasm'] = [
6572
await TestCosmWasmWallet.fromKeyFile(cosmosPrivateKeyPath),
6673
await TestCosmWasmWallet.fromKeyFile(cosmosPrivateKeyPath, 'osmo'),
6774
await TestCosmWasmWallet.fromKeyFile(cosmosPrivateKeyPath, 'neutron'),
6875
]
69-
result['aptos'] = [TestAptosWallet.fromKeyfile(aptosPrivateKeyPath)]
7076
result['injective'] = [
7177
await TestCosmWasmWallet.fromKeyFile(cosmosPrivateKeyPath, 'inj'),
7278
]
7379

74-
result['discord'] = [
75-
DiscordTestWallet.fromKeyfile(TEST_DISCORD_USERNAME, dispenserGuardKeyPath),
76-
]
77-
result['solana'] = [TestSolanaWallet.fromKeyfile(solanaPrivateKeyPath)]
7880
return result
7981
}
8082

@@ -212,18 +214,18 @@ export class TestSolanaWallet implements TestWallet {
212214
}
213215

214216
export class TestAptosWallet implements TestWallet {
215-
constructor(readonly wallet: AptosAccount, readonly addressStr: string) {}
217+
constructor(readonly wallet: AptosAccount) {}
216218
static fromKeyfile(keyFile: string): TestAptosWallet {
217219
const jsonContent = fs.readFileSync(keyFile, 'utf8')
218220
const mnemonic = JSON.parse(jsonContent).mnemonic
219221
const aptosAccount = AptosAccount.fromDerivePath(
220222
"m/44'/637'/0'/0'/0'",
221223
mnemonic
222224
)
223-
return new TestAptosWallet(aptosAccount, aptosAccount.authKey().hex())
225+
return new TestAptosWallet(aptosAccount)
224226
}
225227
address(): string {
226-
return this.addressStr
228+
return this.wallet.authKey().hex()
227229
}
228230

229231
async signMessage(payload: string): Promise<SignedMessage> {
@@ -236,3 +238,24 @@ export class TestAptosWallet implements TestWallet {
236238
)
237239
}
238240
}
241+
242+
export class TestSuiWallet implements TestWallet {
243+
constructor(readonly wallet: Ed25519Keypair) {}
244+
245+
static fromKeyfile(keyFile: string): TestSuiWallet {
246+
const jsonContent = fs.readFileSync(keyFile, 'utf8')
247+
const mnemonic = JSON.parse(jsonContent).mnemonic
248+
return new TestSuiWallet(Ed25519Keypair.deriveKeypair(mnemonic))
249+
}
250+
251+
address(): string {
252+
return this.wallet.toSuiAddress()
253+
}
254+
async signMessage(payload: string): Promise<SignedMessage> {
255+
const response = (
256+
await this.wallet.signPersonalMessage(Buffer.from(payload))
257+
).signature
258+
259+
return suiBuildSignedMessage(response, payload)
260+
}
261+
}

frontend/hooks/useSignMessage.tsx

+2-9
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
evmBuildSignedMessage,
1414
cosmwasmBuildSignedMessage,
1515
aptosBuildSignedMessage,
16+
suiBuildSignedMessage,
1617
} from 'claim_sdk/ecosystems/signatures'
1718

1819
// SignMessageFn signs the message and returns it.
@@ -161,15 +162,7 @@ export function useSuiSignMessage(): SignMessageFn {
161162
message: Buffer.from(payload),
162163
})
163164
).signature
164-
const [signature, publicKey] = splitSignatureAndPubkey(
165-
Buffer.from(response, 'base64')
166-
)
167-
return {
168-
publicKey,
169-
signature,
170-
recoveryId: undefined,
171-
fullMessage: suiGetFullMessage(payload),
172-
}
165+
return suiBuildSignedMessage(response, payload)
173166
} catch (e) {
174167
console.error(e)
175168
}

frontend/integration/integrationTest.test.ts

+44
Original file line numberDiff line numberDiff line change
@@ -417,5 +417,49 @@ describe('integration test', () => {
417417
)
418418
).toBeTruthy()
419419
}, 40000)
420+
421+
it('submits a sui claim', async () => {
422+
const wallet = testWallets.sui[0]
423+
const { claimInfo, proofOfInclusion } = (await mockFetchAmountAndProof(
424+
'sui',
425+
wallet.address()
426+
))!
427+
const signedMessage = await wallet.signMessage(
428+
tokenDispenserProvider.generateAuthorizationPayload()
429+
)
430+
431+
await tokenDispenserProvider.submitClaims([
432+
{
433+
claimInfo,
434+
proofOfInclusion,
435+
signedMessage,
436+
},
437+
])
438+
439+
expect(
440+
await tokenDispenserProvider.isClaimAlreadySubmitted(claimInfo)
441+
).toBeTruthy()
442+
443+
const claimantFundPubkey =
444+
await tokenDispenserProvider.getClaimantFundAddress()
445+
446+
const claimantFund = await mint.getAccountInfo(claimantFundPubkey)
447+
448+
expect(
449+
claimantFund.amount.eq(
450+
new anchor.BN(
451+
3000000 +
452+
6000000 +
453+
6100000 +
454+
6200000 +
455+
7000000 +
456+
5000000 +
457+
1000000 +
458+
2000000 +
459+
4000000
460+
)
461+
)
462+
).toBeTruthy()
463+
}, 40000)
420464
})
421465
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"mnemonic": "pink west stock deputy exhibit opera rely zebra barrel pilot ritual library"
3+
}

0 commit comments

Comments
 (0)