forked from SmartDropLabs/smartdrop-frontend
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsoroban.test.ts
More file actions
141 lines (123 loc) · 4.39 KB
/
Copy pathsoroban.test.ts
File metadata and controls
141 lines (123 loc) · 4.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import { describe, expect, it, vi } from 'vitest';
import {
Account,
Address,
Keypair,
StrKey,
scValToNative,
type xdr,
} from '@stellar/stellar-sdk';
import { amountToStroops, buildLockAssetsTransaction, formatCredits, SorobanService } from './soroban';
vi.mock('@/config', () => ({
factoryContractId: '',
horizonUrl: 'https://horizon-testnet.stellar.org',
networkPassphrase: 'Test SDF Network ; September 2015',
sorobanRpcUrl: 'https://soroban-testnet.stellar.org',
simulationAccount: 'GBQ3WPTHKJ5XKWLOKUZJLZL2GVXR6RWQCXUVDQZWM7Q2YNLDRVGM5ZWJ',
stellarNetwork: 'TESTNET',
}));
const SELECTED_POOL_CONTRACT_ID =
'CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSC4';
type InvokeContractOperation = {
type: 'invokeHostFunction';
func: {
invokeContract: () => {
contractAddress: () => xdr.ScAddress;
functionName: () => string;
args: () => xdr.ScVal[];
};
};
};
describe('buildLockAssetsTransaction', () => {
it('builds a lock_assets contract call with a 7-decimal i128 amount', async () => {
const publicKey = StrKey.encodeEd25519PublicKey(new Uint8Array(32).fill(7));
const keypair = Keypair.fromPublicKey(publicKey);
expect(keypair.publicKey()).toBe(publicKey);
const account = new Account(publicKey, '42');
const rpcServer = {
getAccount: vi.fn().mockResolvedValue(account),
simulateTransaction: vi.fn(),
};
const transaction = await buildLockAssetsTransaction(
{
poolContractId: SELECTED_POOL_CONTRACT_ID,
publicKey,
amount: '1.25',
},
rpcServer,
);
const operation = transaction.operations[0] as unknown as InvokeContractOperation;
const invocation = operation.func.invokeContract();
const args = invocation.args();
expect(rpcServer.getAccount).toHaveBeenCalledWith(publicKey);
expect(operation.type).toBe('invokeHostFunction');
expect(Address.fromScAddress(invocation.contractAddress()).toString()).toBe(
SELECTED_POOL_CONTRACT_ID,
);
expect(invocation.functionName().toString()).toBe('lock_assets');
expect(scValToNative(args[0])).toBe(publicKey);
expect(scValToNative(args[1])).toBe(12500000n);
expect(amountToStroops('1.25')).toBe(12500000n);
});
});
describe('formatCredits', () => {
it.each([
['0', '0'],
['0.0000001', '0.0000001'],
['0.0000123', '0.0000123'],
['0.4200000', '0.42'],
['1.2345678', '1.2346'],
['42.1234567', '42.1235'],
['123.456789', '123.46'],
['999.949999', '999.95'],
['999.95', '1.0K'],
['1000', '1.0K'],
['1250', '1.3K'],
['999949', '999.9K'],
['999950', '1.0M'],
['1000000', '1.0M'],
['2500000', '2.5M'],
])('formats %s as %s', (credits, expected) => {
expect(formatCredits(credits)).toBe(expected);
});
it('never rounds a non-zero balance down to zero', () => {
expect(formatCredits('0.00000001')).toBe('0.00000001');
});
it('preserves loading placeholders and handles malformed values', () => {
expect(formatCredits('-')).toBe('-');
expect(formatCredits('—')).toBe('—');
expect(formatCredits('not-a-number')).toBe('0');
});
});
describe('SorobanService transaction polling', () => {
it('polls NOT_FOUND responses until the transaction succeeds', async () => {
vi.useFakeTimers();
try {
const getTransaction = vi
.fn()
.mockResolvedValueOnce({ status: 'NOT_FOUND' })
.mockResolvedValueOnce({ status: 'NOT_FOUND' })
.mockResolvedValueOnce({ status: 'SUCCESS', resultXdr: 'success-xdr' });
const service = new SorobanService() as unknown as {
rpcServer: { getTransaction: typeof getTransaction };
pollTransactionStatus: (
hash: string,
timeoutMs?: number,
) => Promise<{ status: 'SUCCESS' | 'FAILED' | 'TIMEOUT'; resultXdr?: string }>;
};
service.rpcServer = { getTransaction };
const resultPromise = service.pollTransactionStatus('tx-hash');
await Promise.resolve();
expect(getTransaction).toHaveBeenCalledTimes(1);
await vi.advanceTimersByTimeAsync(1000);
expect(getTransaction).toHaveBeenCalledTimes(2);
await vi.advanceTimersByTimeAsync(2000);
const result = await resultPromise;
expect(result.status).toBe('SUCCESS');
expect(result.resultXdr).toBe('success-xdr');
expect(getTransaction).toHaveBeenCalledTimes(3);
} finally {
vi.useRealTimers();
}
});
});