@mania-labs/mania-sdk 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +22 -17
- package/dist/index.mjs +22 -17
- package/package.json +7 -1
- package/src/.claude/settings.local.json +2 -1
- package/src/__tests__/helpers/fixtures.ts +136 -0
- package/src/__tests__/helpers/integrationHelpers.ts +258 -0
- package/src/__tests__/helpers/mocks.ts +184 -0
- package/src/__tests__/integration/sdk-read.test.ts +196 -0
- package/src/__tests__/integration/sdk-write.test.ts +383 -0
- package/src/__tests__/setup.ts +34 -0
- package/src/__tests__/unit/bondingCurve.test.ts +357 -0
- package/src/__tests__/unit/constants.test.ts +136 -0
- package/src/__tests__/unit/mania.test.ts +495 -0
- package/src/__tests__/unit/utils.test.ts +328 -0
- package/src/constants.ts +2 -2
- package/src/mania.ts +15 -10
package/dist/index.js
CHANGED
|
@@ -76,8 +76,8 @@ var DEFAULT_SLIPPAGE_BPS = 100;
|
|
|
76
76
|
var BPS_DENOMINATOR = 10000n;
|
|
77
77
|
var CHAIN_CONFIGS = {
|
|
78
78
|
// Mega Eth Testnet
|
|
79
|
-
|
|
80
|
-
chainId:
|
|
79
|
+
6343: {
|
|
80
|
+
chainId: 6343,
|
|
81
81
|
name: "Mega Eth Testnet",
|
|
82
82
|
factoryAddress: "0x0d593cE47EBA2d15a77ddbAc41BdE6d03CC9241b",
|
|
83
83
|
wethAddress: "0x4200000000000000000000000000000000000006",
|
|
@@ -815,12 +815,13 @@ var ManiaSDK = class _ManiaSDK {
|
|
|
815
815
|
async create(params) {
|
|
816
816
|
const wallet = this.getConnectedWallet();
|
|
817
817
|
const hash = await wallet.writeContract({
|
|
818
|
-
chain:
|
|
819
|
-
account:
|
|
818
|
+
chain: wallet.chain,
|
|
819
|
+
account: wallet.account,
|
|
820
820
|
address: this.factoryAddress,
|
|
821
821
|
abi: MANIA_FACTORY_ABI,
|
|
822
822
|
functionName: "create",
|
|
823
|
-
args: [params.name, params.symbol, params.uri, params.creator]
|
|
823
|
+
args: [params.name, params.symbol, params.uri, params.creator],
|
|
824
|
+
gas: 300000000n
|
|
824
825
|
});
|
|
825
826
|
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
826
827
|
const logs = (0, import_viem.parseEventLogs)({
|
|
@@ -844,13 +845,14 @@ var ManiaSDK = class _ManiaSDK {
|
|
|
844
845
|
async createAndBuy(params) {
|
|
845
846
|
const wallet = this.getConnectedWallet();
|
|
846
847
|
const hash = await wallet.writeContract({
|
|
847
|
-
chain:
|
|
848
|
-
account:
|
|
848
|
+
chain: wallet.chain,
|
|
849
|
+
account: wallet.account,
|
|
849
850
|
address: this.factoryAddress,
|
|
850
851
|
abi: MANIA_FACTORY_ABI,
|
|
851
852
|
functionName: "createAndBuy",
|
|
852
853
|
args: [params.name, params.symbol, params.uri, params.creator, params.minTokensOut],
|
|
853
|
-
value: params.buyAmountEth
|
|
854
|
+
value: params.buyAmountEth,
|
|
855
|
+
gas: 300000000n
|
|
854
856
|
});
|
|
855
857
|
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
856
858
|
const logs = (0, import_viem.parseEventLogs)({
|
|
@@ -875,13 +877,14 @@ var ManiaSDK = class _ManiaSDK {
|
|
|
875
877
|
const wallet = this.getConnectedWallet();
|
|
876
878
|
const recipient = params.recipient ?? wallet.account.address;
|
|
877
879
|
const hash = await wallet.writeContract({
|
|
878
|
-
chain:
|
|
879
|
-
account:
|
|
880
|
+
chain: wallet.chain,
|
|
881
|
+
account: wallet.account,
|
|
880
882
|
address: this.factoryAddress,
|
|
881
883
|
abi: MANIA_FACTORY_ABI,
|
|
882
884
|
functionName: "buy",
|
|
883
885
|
args: [params.token, params.minTokensOut, recipient],
|
|
884
|
-
value: params.amountEth
|
|
886
|
+
value: params.amountEth,
|
|
887
|
+
gas: 300000000n
|
|
885
888
|
});
|
|
886
889
|
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
887
890
|
return {
|
|
@@ -914,12 +917,13 @@ var ManiaSDK = class _ManiaSDK {
|
|
|
914
917
|
async sell(params) {
|
|
915
918
|
const wallet = this.getConnectedWallet();
|
|
916
919
|
const hash = await wallet.writeContract({
|
|
917
|
-
chain:
|
|
918
|
-
account:
|
|
920
|
+
chain: wallet.chain,
|
|
921
|
+
account: wallet.account,
|
|
919
922
|
address: this.factoryAddress,
|
|
920
923
|
abi: MANIA_FACTORY_ABI,
|
|
921
924
|
functionName: "sell",
|
|
922
|
-
args: [params.token, params.amountTokens, params.minEthOut]
|
|
925
|
+
args: [params.token, params.amountTokens, params.minEthOut],
|
|
926
|
+
gas: 300000000n
|
|
923
927
|
});
|
|
924
928
|
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
925
929
|
return {
|
|
@@ -953,13 +957,14 @@ var ManiaSDK = class _ManiaSDK {
|
|
|
953
957
|
const wallet = this.getConnectedWallet();
|
|
954
958
|
const globalState = await this.getGlobalState();
|
|
955
959
|
const hash = await wallet.writeContract({
|
|
956
|
-
chain:
|
|
957
|
-
account:
|
|
960
|
+
chain: wallet.chain,
|
|
961
|
+
account: wallet.account,
|
|
958
962
|
address: this.factoryAddress,
|
|
959
963
|
abi: MANIA_FACTORY_ABI,
|
|
960
964
|
functionName: "migrate",
|
|
961
965
|
args: [params.token],
|
|
962
|
-
value: globalState.poolMigrationFee
|
|
966
|
+
value: globalState.poolMigrationFee,
|
|
967
|
+
gas: 300000000n
|
|
963
968
|
});
|
|
964
969
|
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
965
970
|
const logs = (0, import_viem.parseEventLogs)({
|
package/dist/index.mjs
CHANGED
|
@@ -22,8 +22,8 @@ var DEFAULT_SLIPPAGE_BPS = 100;
|
|
|
22
22
|
var BPS_DENOMINATOR = 10000n;
|
|
23
23
|
var CHAIN_CONFIGS = {
|
|
24
24
|
// Mega Eth Testnet
|
|
25
|
-
|
|
26
|
-
chainId:
|
|
25
|
+
6343: {
|
|
26
|
+
chainId: 6343,
|
|
27
27
|
name: "Mega Eth Testnet",
|
|
28
28
|
factoryAddress: "0x0d593cE47EBA2d15a77ddbAc41BdE6d03CC9241b",
|
|
29
29
|
wethAddress: "0x4200000000000000000000000000000000000006",
|
|
@@ -761,12 +761,13 @@ var ManiaSDK = class _ManiaSDK {
|
|
|
761
761
|
async create(params) {
|
|
762
762
|
const wallet = this.getConnectedWallet();
|
|
763
763
|
const hash = await wallet.writeContract({
|
|
764
|
-
chain:
|
|
765
|
-
account:
|
|
764
|
+
chain: wallet.chain,
|
|
765
|
+
account: wallet.account,
|
|
766
766
|
address: this.factoryAddress,
|
|
767
767
|
abi: MANIA_FACTORY_ABI,
|
|
768
768
|
functionName: "create",
|
|
769
|
-
args: [params.name, params.symbol, params.uri, params.creator]
|
|
769
|
+
args: [params.name, params.symbol, params.uri, params.creator],
|
|
770
|
+
gas: 300000000n
|
|
770
771
|
});
|
|
771
772
|
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
772
773
|
const logs = parseEventLogs({
|
|
@@ -790,13 +791,14 @@ var ManiaSDK = class _ManiaSDK {
|
|
|
790
791
|
async createAndBuy(params) {
|
|
791
792
|
const wallet = this.getConnectedWallet();
|
|
792
793
|
const hash = await wallet.writeContract({
|
|
793
|
-
chain:
|
|
794
|
-
account:
|
|
794
|
+
chain: wallet.chain,
|
|
795
|
+
account: wallet.account,
|
|
795
796
|
address: this.factoryAddress,
|
|
796
797
|
abi: MANIA_FACTORY_ABI,
|
|
797
798
|
functionName: "createAndBuy",
|
|
798
799
|
args: [params.name, params.symbol, params.uri, params.creator, params.minTokensOut],
|
|
799
|
-
value: params.buyAmountEth
|
|
800
|
+
value: params.buyAmountEth,
|
|
801
|
+
gas: 300000000n
|
|
800
802
|
});
|
|
801
803
|
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
802
804
|
const logs = parseEventLogs({
|
|
@@ -821,13 +823,14 @@ var ManiaSDK = class _ManiaSDK {
|
|
|
821
823
|
const wallet = this.getConnectedWallet();
|
|
822
824
|
const recipient = params.recipient ?? wallet.account.address;
|
|
823
825
|
const hash = await wallet.writeContract({
|
|
824
|
-
chain:
|
|
825
|
-
account:
|
|
826
|
+
chain: wallet.chain,
|
|
827
|
+
account: wallet.account,
|
|
826
828
|
address: this.factoryAddress,
|
|
827
829
|
abi: MANIA_FACTORY_ABI,
|
|
828
830
|
functionName: "buy",
|
|
829
831
|
args: [params.token, params.minTokensOut, recipient],
|
|
830
|
-
value: params.amountEth
|
|
832
|
+
value: params.amountEth,
|
|
833
|
+
gas: 300000000n
|
|
831
834
|
});
|
|
832
835
|
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
833
836
|
return {
|
|
@@ -860,12 +863,13 @@ var ManiaSDK = class _ManiaSDK {
|
|
|
860
863
|
async sell(params) {
|
|
861
864
|
const wallet = this.getConnectedWallet();
|
|
862
865
|
const hash = await wallet.writeContract({
|
|
863
|
-
chain:
|
|
864
|
-
account:
|
|
866
|
+
chain: wallet.chain,
|
|
867
|
+
account: wallet.account,
|
|
865
868
|
address: this.factoryAddress,
|
|
866
869
|
abi: MANIA_FACTORY_ABI,
|
|
867
870
|
functionName: "sell",
|
|
868
|
-
args: [params.token, params.amountTokens, params.minEthOut]
|
|
871
|
+
args: [params.token, params.amountTokens, params.minEthOut],
|
|
872
|
+
gas: 300000000n
|
|
869
873
|
});
|
|
870
874
|
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
871
875
|
return {
|
|
@@ -899,13 +903,14 @@ var ManiaSDK = class _ManiaSDK {
|
|
|
899
903
|
const wallet = this.getConnectedWallet();
|
|
900
904
|
const globalState = await this.getGlobalState();
|
|
901
905
|
const hash = await wallet.writeContract({
|
|
902
|
-
chain:
|
|
903
|
-
account:
|
|
906
|
+
chain: wallet.chain,
|
|
907
|
+
account: wallet.account,
|
|
904
908
|
address: this.factoryAddress,
|
|
905
909
|
abi: MANIA_FACTORY_ABI,
|
|
906
910
|
functionName: "migrate",
|
|
907
911
|
args: [params.token],
|
|
908
|
-
value: globalState.poolMigrationFee
|
|
912
|
+
value: globalState.poolMigrationFee,
|
|
913
|
+
gas: 300000000n
|
|
909
914
|
});
|
|
910
915
|
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
911
916
|
const logs = parseEventLogs({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mania-labs/mania-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Official SDK for interacting with the Mania Protocol - EVM Bonding Curve Token Launchpad",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
25
25
|
"lint": "eslint src --ext .ts",
|
|
26
26
|
"test": "vitest",
|
|
27
|
+
"test:unit": "vitest run --dir src/__tests__/unit",
|
|
28
|
+
"test:integration": "vitest run --dir src/__tests__/integration",
|
|
29
|
+
"test:coverage": "vitest run --coverage",
|
|
30
|
+
"test:watch": "vitest watch",
|
|
27
31
|
"prepublishOnly": "npm run build"
|
|
28
32
|
},
|
|
29
33
|
"keywords": [
|
|
@@ -43,6 +47,8 @@
|
|
|
43
47
|
},
|
|
44
48
|
"devDependencies": {
|
|
45
49
|
"@types/node": "^24.10.1",
|
|
50
|
+
"@vitest/coverage-v8": "^2.1.0",
|
|
51
|
+
"dotenv": "^16.3.1",
|
|
46
52
|
"tsup": "^8.5.1",
|
|
47
53
|
"typescript": "^5.9.3",
|
|
48
54
|
"vitest": "^2.1.0"
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { Address } from 'viem';
|
|
2
|
+
import { parseEther } from 'viem';
|
|
3
|
+
import type { BondingCurveState, GlobalState } from '../../types.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Test addresses
|
|
7
|
+
*/
|
|
8
|
+
export const TEST_ADDRESSES = {
|
|
9
|
+
FACTORY: '0x0d593cE47EBA2d15a77ddbAc41BdE6d03CC9241b' as Address,
|
|
10
|
+
WETH: '0x4200000000000000000000000000000000000006' as Address,
|
|
11
|
+
ZERO: '0x0000000000000000000000000000000000000000' as Address,
|
|
12
|
+
USER1: '0x1111111111111111111111111111111111111111' as Address,
|
|
13
|
+
USER2: '0x2222222222222222222222222222222222222222' as Address,
|
|
14
|
+
TOKEN1: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' as Address,
|
|
15
|
+
TOKEN2: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' as Address,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Common test amounts
|
|
20
|
+
*/
|
|
21
|
+
export const TEST_AMOUNTS = {
|
|
22
|
+
SMALL_ETH: parseEther('0.01'),
|
|
23
|
+
MEDIUM_ETH: parseEther('0.1'),
|
|
24
|
+
LARGE_ETH: parseEther('1'),
|
|
25
|
+
MIGRATION_THRESHOLD: parseEther('4'),
|
|
26
|
+
SMALL_TOKENS: parseEther('1000'),
|
|
27
|
+
MEDIUM_TOKENS: parseEther('1000000'),
|
|
28
|
+
LARGE_TOKENS: parseEther('100000000'),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Default token creation parameters
|
|
33
|
+
*/
|
|
34
|
+
export const TEST_TOKEN_PARAMS = {
|
|
35
|
+
name: 'Test Token',
|
|
36
|
+
symbol: 'TEST',
|
|
37
|
+
uri: 'https://example.com/metadata.json',
|
|
38
|
+
creator: TEST_ADDRESSES.USER1,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Initial virtual reserves (matching contract defaults)
|
|
43
|
+
*/
|
|
44
|
+
export const INITIAL_RESERVES = {
|
|
45
|
+
virtualTokenReserves: 1_073_000_000_000_000_000_000_000_000n, // 1.073B tokens
|
|
46
|
+
virtualEthReserves: 30_000_000_000_000_000n, // 0.03 ETH
|
|
47
|
+
realTokenReserves: 793_100_000_000_000_000_000_000_000n, // 793.1M tokens
|
|
48
|
+
tokenTotalSupply: 1_000_000_000_000_000_000_000_000_000n, // 1B tokens
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Pre-configured bonding curve scenarios
|
|
53
|
+
*/
|
|
54
|
+
export const CURVE_SCENARIOS = {
|
|
55
|
+
/**
|
|
56
|
+
* Fresh curve - no trades yet
|
|
57
|
+
*/
|
|
58
|
+
FRESH: {
|
|
59
|
+
virtualTokenReserves: INITIAL_RESERVES.virtualTokenReserves,
|
|
60
|
+
virtualEthReserves: INITIAL_RESERVES.virtualEthReserves,
|
|
61
|
+
realTokenReserves: INITIAL_RESERVES.realTokenReserves,
|
|
62
|
+
realEthReserves: 0n,
|
|
63
|
+
tokenTotalSupply: INITIAL_RESERVES.tokenTotalSupply,
|
|
64
|
+
complete: false,
|
|
65
|
+
trackVolume: true,
|
|
66
|
+
} as BondingCurveState,
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Half-filled curve - 2 ETH real reserves (50% to migration)
|
|
70
|
+
*/
|
|
71
|
+
HALF_FILLED: {
|
|
72
|
+
virtualTokenReserves: 800_000_000_000_000_000_000_000_000n,
|
|
73
|
+
virtualEthReserves: 2_030_000_000_000_000_000n, // 2.03 ETH
|
|
74
|
+
realTokenReserves: 520_000_000_000_000_000_000_000_000n,
|
|
75
|
+
realEthReserves: 2_000_000_000_000_000_000n, // 2 ETH
|
|
76
|
+
tokenTotalSupply: INITIAL_RESERVES.tokenTotalSupply,
|
|
77
|
+
complete: false,
|
|
78
|
+
trackVolume: true,
|
|
79
|
+
} as BondingCurveState,
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Near-complete curve - 3.9 ETH real reserves
|
|
83
|
+
*/
|
|
84
|
+
NEAR_COMPLETE: {
|
|
85
|
+
virtualTokenReserves: 300_000_000_000_000_000_000_000_000n,
|
|
86
|
+
virtualEthReserves: 3_930_000_000_000_000_000n,
|
|
87
|
+
realTokenReserves: 27_000_000_000_000_000_000_000_000n,
|
|
88
|
+
realEthReserves: 3_900_000_000_000_000_000n, // 3.9 ETH
|
|
89
|
+
tokenTotalSupply: INITIAL_RESERVES.tokenTotalSupply,
|
|
90
|
+
complete: false,
|
|
91
|
+
trackVolume: true,
|
|
92
|
+
} as BondingCurveState,
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Complete curve - reached 4 ETH threshold
|
|
96
|
+
*/
|
|
97
|
+
COMPLETE: {
|
|
98
|
+
virtualTokenReserves: 280_000_000_000_000_000_000_000_000n,
|
|
99
|
+
virtualEthReserves: 4_030_000_000_000_000_000n,
|
|
100
|
+
realTokenReserves: 7_000_000_000_000_000_000_000_000n,
|
|
101
|
+
realEthReserves: 4_000_000_000_000_000_000n, // 4 ETH
|
|
102
|
+
tokenTotalSupply: INITIAL_RESERVES.tokenTotalSupply,
|
|
103
|
+
complete: true,
|
|
104
|
+
trackVolume: true,
|
|
105
|
+
} as BondingCurveState,
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Migrated curve - all reserves zeroed out
|
|
109
|
+
*/
|
|
110
|
+
MIGRATED: {
|
|
111
|
+
virtualTokenReserves: 0n,
|
|
112
|
+
virtualEthReserves: 0n,
|
|
113
|
+
realTokenReserves: 0n,
|
|
114
|
+
realEthReserves: 0n,
|
|
115
|
+
tokenTotalSupply: INITIAL_RESERVES.tokenTotalSupply,
|
|
116
|
+
complete: true,
|
|
117
|
+
trackVolume: true,
|
|
118
|
+
} as BondingCurveState,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Default global state
|
|
123
|
+
*/
|
|
124
|
+
export const DEFAULT_GLOBAL_STATE: GlobalState = {
|
|
125
|
+
initialized: true,
|
|
126
|
+
authority: TEST_ADDRESSES.USER1,
|
|
127
|
+
feeRecipient: TEST_ADDRESSES.USER1,
|
|
128
|
+
initialVirtualTokenReserves: INITIAL_RESERVES.virtualTokenReserves,
|
|
129
|
+
initialVirtualEthReserves: INITIAL_RESERVES.virtualEthReserves,
|
|
130
|
+
initialRealTokenReserves: INITIAL_RESERVES.realTokenReserves,
|
|
131
|
+
tokenTotalSupply: INITIAL_RESERVES.tokenTotalSupply,
|
|
132
|
+
feeBasisPoints: 100n, // 1%
|
|
133
|
+
withdrawAuthority: TEST_ADDRESSES.USER1,
|
|
134
|
+
enableMigrate: true,
|
|
135
|
+
poolMigrationFee: 300_000_000_000_000n, // 0.0003 ETH
|
|
136
|
+
};
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createPublicClient,
|
|
3
|
+
createWalletClient,
|
|
4
|
+
http,
|
|
5
|
+
type Address,
|
|
6
|
+
type PublicClient,
|
|
7
|
+
type WalletClient,
|
|
8
|
+
type Hash,
|
|
9
|
+
formatEther,
|
|
10
|
+
parseEther,
|
|
11
|
+
} from 'viem';
|
|
12
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
13
|
+
import { ManiaSDK } from '../../mania.js';
|
|
14
|
+
import { CHAIN_CONFIGS } from '../../constants.js';
|
|
15
|
+
import { ERC20_ABI } from '../../abi.js';
|
|
16
|
+
|
|
17
|
+
// Mega ETH Testnet chain definition
|
|
18
|
+
const megaEthTestnet = {
|
|
19
|
+
id: 6343,
|
|
20
|
+
name: 'Mega ETH Testnet',
|
|
21
|
+
nativeCurrency: {
|
|
22
|
+
decimals: 18,
|
|
23
|
+
name: 'Ether',
|
|
24
|
+
symbol: 'ETH',
|
|
25
|
+
},
|
|
26
|
+
rpcUrls: {
|
|
27
|
+
default: {
|
|
28
|
+
http: [process.env.MEGA_ETH_RPC_URL || 'https://timothy.megaeth.com/rpc'],
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
blockExplorers: {
|
|
32
|
+
default: {
|
|
33
|
+
name: 'Blockscout',
|
|
34
|
+
url: 'https://megaeth-testnet-v2.blockscout.com',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
} as const;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Balance snapshot for tracking changes
|
|
41
|
+
*/
|
|
42
|
+
export interface BalanceSnapshot {
|
|
43
|
+
eth: bigint;
|
|
44
|
+
token?: bigint;
|
|
45
|
+
tokenAddress?: Address;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Balance comparison result
|
|
50
|
+
*/
|
|
51
|
+
export interface BalanceComparison {
|
|
52
|
+
ethDiff: bigint;
|
|
53
|
+
tokenDiff?: bigint;
|
|
54
|
+
ethDiffFormatted: string;
|
|
55
|
+
tokenDiffFormatted?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Create SDK instance for integration tests
|
|
60
|
+
*/
|
|
61
|
+
export function createTestSDK(): {
|
|
62
|
+
sdk: ManiaSDK;
|
|
63
|
+
walletAddress: Address;
|
|
64
|
+
publicClient: PublicClient;
|
|
65
|
+
walletClient: WalletClient;
|
|
66
|
+
} {
|
|
67
|
+
const privateKey = process.env.TEST_PRIVATE_KEY;
|
|
68
|
+
if (!privateKey) {
|
|
69
|
+
throw new Error('TEST_PRIVATE_KEY environment variable is required');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const rpcUrl = process.env.MEGA_ETH_RPC_URL || 'https://timothy.megaeth.com/rpc';
|
|
73
|
+
const chainConfig = CHAIN_CONFIGS[6343];
|
|
74
|
+
|
|
75
|
+
if (!chainConfig) {
|
|
76
|
+
throw new Error('Mega ETH Testnet chain config not found');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const account = privateKeyToAccount(
|
|
80
|
+
privateKey.startsWith('0x')
|
|
81
|
+
? (privateKey as `0x${string}`)
|
|
82
|
+
: (`0x${privateKey}` as `0x${string}`)
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const publicClient = createPublicClient({
|
|
86
|
+
chain: megaEthTestnet,
|
|
87
|
+
transport: http(rpcUrl),
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const walletClient = createWalletClient({
|
|
91
|
+
account,
|
|
92
|
+
chain: megaEthTestnet,
|
|
93
|
+
transport: http(rpcUrl),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const sdk = new ManiaSDK({
|
|
97
|
+
factoryAddress: chainConfig.factoryAddress,
|
|
98
|
+
rpcUrl,
|
|
99
|
+
chainId: 6343,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
sdk.setPublicClient(publicClient as unknown as PublicClient);
|
|
103
|
+
sdk.setWalletClient(walletClient as unknown as WalletClient);
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
sdk,
|
|
107
|
+
walletAddress: account.address,
|
|
108
|
+
publicClient: publicClient as unknown as PublicClient,
|
|
109
|
+
walletClient: walletClient as unknown as WalletClient,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Get ETH and optional token balance snapshot
|
|
115
|
+
*/
|
|
116
|
+
export async function getBalanceSnapshot(
|
|
117
|
+
publicClient: PublicClient,
|
|
118
|
+
walletAddress: Address,
|
|
119
|
+
tokenAddress?: Address
|
|
120
|
+
): Promise<BalanceSnapshot> {
|
|
121
|
+
const eth = await publicClient.getBalance({ address: walletAddress });
|
|
122
|
+
|
|
123
|
+
let token: bigint | undefined;
|
|
124
|
+
if (tokenAddress) {
|
|
125
|
+
token = await getTokenBalance(publicClient, tokenAddress, walletAddress);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
eth,
|
|
130
|
+
token,
|
|
131
|
+
tokenAddress,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Compare two balance snapshots
|
|
137
|
+
*/
|
|
138
|
+
export function compareBalances(
|
|
139
|
+
before: BalanceSnapshot,
|
|
140
|
+
after: BalanceSnapshot
|
|
141
|
+
): BalanceComparison {
|
|
142
|
+
const ethDiff = after.eth - before.eth;
|
|
143
|
+
|
|
144
|
+
let tokenDiff: bigint | undefined;
|
|
145
|
+
let tokenDiffFormatted: string | undefined;
|
|
146
|
+
|
|
147
|
+
if (before.token !== undefined && after.token !== undefined) {
|
|
148
|
+
tokenDiff = after.token - before.token;
|
|
149
|
+
tokenDiffFormatted = formatEther(tokenDiff);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
ethDiff,
|
|
154
|
+
tokenDiff,
|
|
155
|
+
ethDiffFormatted: formatEther(ethDiff),
|
|
156
|
+
tokenDiffFormatted,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Get ERC20 token balance
|
|
162
|
+
*/
|
|
163
|
+
export async function getTokenBalance(
|
|
164
|
+
publicClient: PublicClient,
|
|
165
|
+
tokenAddress: Address,
|
|
166
|
+
walletAddress: Address
|
|
167
|
+
): Promise<bigint> {
|
|
168
|
+
const balance = await publicClient.readContract({
|
|
169
|
+
address: tokenAddress,
|
|
170
|
+
abi: ERC20_ABI,
|
|
171
|
+
functionName: 'balanceOf',
|
|
172
|
+
args: [walletAddress],
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
return balance as bigint;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Verify a transaction was successful
|
|
180
|
+
*/
|
|
181
|
+
export async function verifyTransactionSuccess(
|
|
182
|
+
publicClient: PublicClient,
|
|
183
|
+
hash: Hash
|
|
184
|
+
): Promise<boolean> {
|
|
185
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
186
|
+
return receipt.status === 'success';
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Check if wallet has enough ETH
|
|
191
|
+
*/
|
|
192
|
+
export async function hasEnoughETH(
|
|
193
|
+
publicClient: PublicClient,
|
|
194
|
+
walletAddress: Address,
|
|
195
|
+
requiredAmount: bigint,
|
|
196
|
+
buffer: bigint = parseEther('0.01') // Buffer for gas
|
|
197
|
+
): Promise<boolean> {
|
|
198
|
+
const balance = await publicClient.getBalance({ address: walletAddress });
|
|
199
|
+
return balance >= requiredAmount + buffer;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Generate unique token parameters for each test
|
|
204
|
+
*/
|
|
205
|
+
export function generateUniqueTokenParams() {
|
|
206
|
+
const timestamp = Date.now();
|
|
207
|
+
const random = Math.floor(Math.random() * 10000);
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
name: `Test Token ${timestamp}-${random}`,
|
|
211
|
+
symbol: `T${random}`,
|
|
212
|
+
uri: `https://example.com/token/${timestamp}-${random}.json`,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Retry wrapper for flaky network operations
|
|
218
|
+
*/
|
|
219
|
+
export async function withTestRetry<T>(
|
|
220
|
+
fn: () => Promise<T>,
|
|
221
|
+
maxRetries: number = 3,
|
|
222
|
+
delayMs: number = 1000
|
|
223
|
+
): Promise<T> {
|
|
224
|
+
let lastError: Error | undefined;
|
|
225
|
+
|
|
226
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
227
|
+
try {
|
|
228
|
+
return await fn();
|
|
229
|
+
} catch (error) {
|
|
230
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
231
|
+
if (i < maxRetries - 1) {
|
|
232
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs * (i + 1)));
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
throw lastError;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Wait for a condition with timeout
|
|
242
|
+
*/
|
|
243
|
+
export async function waitForCondition(
|
|
244
|
+
condition: () => Promise<boolean>,
|
|
245
|
+
timeoutMs: number = 30000,
|
|
246
|
+
intervalMs: number = 1000
|
|
247
|
+
): Promise<boolean> {
|
|
248
|
+
const startTime = Date.now();
|
|
249
|
+
|
|
250
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
251
|
+
if (await condition()) {
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return false;
|
|
258
|
+
}
|