@mania-labs/mania-sdk 1.0.0 → 1.0.2
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/README.md +5 -5
- package/dist/index.js +32 -17
- package/dist/index.mjs +32 -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 +13 -2
- package/src/mania.ts +15 -10
package/README.md
CHANGED
|
@@ -280,12 +280,12 @@ console.log(UNISWAP_FEE_TIER); // 3000
|
|
|
280
280
|
import { getChainConfig, CHAIN_CONFIGS } from "@mania-labs/mania-sdk";
|
|
281
281
|
|
|
282
282
|
// Get config for a specific chain
|
|
283
|
-
const baseConfig = getChainConfig(
|
|
283
|
+
const baseConfig = getChainConfig(4326);
|
|
284
284
|
console.log(baseConfig?.factoryAddress);
|
|
285
285
|
console.log(baseConfig?.wethAddress);
|
|
286
286
|
|
|
287
287
|
// Or create SDK from chain ID
|
|
288
|
-
const sdk = ManiaSDK.fromChainId(
|
|
288
|
+
const sdk = ManiaSDK.fromChainId(4326, "https://megaeth.blockscout.com");
|
|
289
289
|
```
|
|
290
290
|
|
|
291
291
|
## Advanced Usage
|
|
@@ -299,18 +299,18 @@ import { privateKeyToAccount } from "viem/accounts";
|
|
|
299
299
|
|
|
300
300
|
const publicClient = createPublicClient({
|
|
301
301
|
chain: base,
|
|
302
|
-
transport: http("https://
|
|
302
|
+
transport: http("https://megaeth.blockscout.com"),
|
|
303
303
|
});
|
|
304
304
|
|
|
305
305
|
const walletClient = createWalletClient({
|
|
306
306
|
account: privateKeyToAccount("0x..."),
|
|
307
307
|
chain: base,
|
|
308
|
-
transport: http("https://
|
|
308
|
+
transport: http("https://megaeth.blockscout.com"),
|
|
309
309
|
});
|
|
310
310
|
|
|
311
311
|
const sdk = new ManiaSDK({
|
|
312
312
|
factoryAddress: "0x...",
|
|
313
|
-
chainId:
|
|
313
|
+
chainId: 4326,
|
|
314
314
|
});
|
|
315
315
|
|
|
316
316
|
sdk.setPublicClient(publicClient);
|
package/dist/index.js
CHANGED
|
@@ -76,14 +76,24 @@ 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",
|
|
84
84
|
nonfungiblePositionManager: "0xa204A97EF8Bd2E3198f19EB5a804680467BD85f5",
|
|
85
85
|
uniswapV3Factory: "0x619fb6C12c36b57a8bAb05e98F42C43745DCf69f",
|
|
86
86
|
blockExplorer: "https://megaeth-testnet-v2.blockscout.com"
|
|
87
|
+
},
|
|
88
|
+
// Mega Eth Mainnet
|
|
89
|
+
4326: {
|
|
90
|
+
chainId: 4326,
|
|
91
|
+
name: "Mega Eth Mainnet",
|
|
92
|
+
factoryAddress: "0xb2f6344Cb0Ee13e027759166770198f4d8B3106d",
|
|
93
|
+
wethAddress: "0x4200000000000000000000000000000000000006",
|
|
94
|
+
nonfungiblePositionManager: "0x2b781C57e6358f64864Ff8EC464a03Fdaf9974bA",
|
|
95
|
+
uniswapV3Factory: "0x68b34591f662508076927803c567Cc8006988a09",
|
|
96
|
+
blockExplorer: "https://megaeth.blockscout.com/"
|
|
87
97
|
}
|
|
88
98
|
};
|
|
89
99
|
function getChainConfig(chainId) {
|
|
@@ -815,12 +825,13 @@ var ManiaSDK = class _ManiaSDK {
|
|
|
815
825
|
async create(params) {
|
|
816
826
|
const wallet = this.getConnectedWallet();
|
|
817
827
|
const hash = await wallet.writeContract({
|
|
818
|
-
chain:
|
|
819
|
-
account:
|
|
828
|
+
chain: wallet.chain,
|
|
829
|
+
account: wallet.account,
|
|
820
830
|
address: this.factoryAddress,
|
|
821
831
|
abi: MANIA_FACTORY_ABI,
|
|
822
832
|
functionName: "create",
|
|
823
|
-
args: [params.name, params.symbol, params.uri, params.creator]
|
|
833
|
+
args: [params.name, params.symbol, params.uri, params.creator],
|
|
834
|
+
gas: 300000000n
|
|
824
835
|
});
|
|
825
836
|
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
826
837
|
const logs = (0, import_viem.parseEventLogs)({
|
|
@@ -844,13 +855,14 @@ var ManiaSDK = class _ManiaSDK {
|
|
|
844
855
|
async createAndBuy(params) {
|
|
845
856
|
const wallet = this.getConnectedWallet();
|
|
846
857
|
const hash = await wallet.writeContract({
|
|
847
|
-
chain:
|
|
848
|
-
account:
|
|
858
|
+
chain: wallet.chain,
|
|
859
|
+
account: wallet.account,
|
|
849
860
|
address: this.factoryAddress,
|
|
850
861
|
abi: MANIA_FACTORY_ABI,
|
|
851
862
|
functionName: "createAndBuy",
|
|
852
863
|
args: [params.name, params.symbol, params.uri, params.creator, params.minTokensOut],
|
|
853
|
-
value: params.buyAmountEth
|
|
864
|
+
value: params.buyAmountEth,
|
|
865
|
+
gas: 300000000n
|
|
854
866
|
});
|
|
855
867
|
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
856
868
|
const logs = (0, import_viem.parseEventLogs)({
|
|
@@ -875,13 +887,14 @@ var ManiaSDK = class _ManiaSDK {
|
|
|
875
887
|
const wallet = this.getConnectedWallet();
|
|
876
888
|
const recipient = params.recipient ?? wallet.account.address;
|
|
877
889
|
const hash = await wallet.writeContract({
|
|
878
|
-
chain:
|
|
879
|
-
account:
|
|
890
|
+
chain: wallet.chain,
|
|
891
|
+
account: wallet.account,
|
|
880
892
|
address: this.factoryAddress,
|
|
881
893
|
abi: MANIA_FACTORY_ABI,
|
|
882
894
|
functionName: "buy",
|
|
883
895
|
args: [params.token, params.minTokensOut, recipient],
|
|
884
|
-
value: params.amountEth
|
|
896
|
+
value: params.amountEth,
|
|
897
|
+
gas: 300000000n
|
|
885
898
|
});
|
|
886
899
|
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
887
900
|
return {
|
|
@@ -914,12 +927,13 @@ var ManiaSDK = class _ManiaSDK {
|
|
|
914
927
|
async sell(params) {
|
|
915
928
|
const wallet = this.getConnectedWallet();
|
|
916
929
|
const hash = await wallet.writeContract({
|
|
917
|
-
chain:
|
|
918
|
-
account:
|
|
930
|
+
chain: wallet.chain,
|
|
931
|
+
account: wallet.account,
|
|
919
932
|
address: this.factoryAddress,
|
|
920
933
|
abi: MANIA_FACTORY_ABI,
|
|
921
934
|
functionName: "sell",
|
|
922
|
-
args: [params.token, params.amountTokens, params.minEthOut]
|
|
935
|
+
args: [params.token, params.amountTokens, params.minEthOut],
|
|
936
|
+
gas: 300000000n
|
|
923
937
|
});
|
|
924
938
|
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
925
939
|
return {
|
|
@@ -953,13 +967,14 @@ var ManiaSDK = class _ManiaSDK {
|
|
|
953
967
|
const wallet = this.getConnectedWallet();
|
|
954
968
|
const globalState = await this.getGlobalState();
|
|
955
969
|
const hash = await wallet.writeContract({
|
|
956
|
-
chain:
|
|
957
|
-
account:
|
|
970
|
+
chain: wallet.chain,
|
|
971
|
+
account: wallet.account,
|
|
958
972
|
address: this.factoryAddress,
|
|
959
973
|
abi: MANIA_FACTORY_ABI,
|
|
960
974
|
functionName: "migrate",
|
|
961
975
|
args: [params.token],
|
|
962
|
-
value: globalState.poolMigrationFee
|
|
976
|
+
value: globalState.poolMigrationFee,
|
|
977
|
+
gas: 300000000n
|
|
963
978
|
});
|
|
964
979
|
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
965
980
|
const logs = (0, import_viem.parseEventLogs)({
|
package/dist/index.mjs
CHANGED
|
@@ -22,14 +22,24 @@ 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",
|
|
30
30
|
nonfungiblePositionManager: "0xa204A97EF8Bd2E3198f19EB5a804680467BD85f5",
|
|
31
31
|
uniswapV3Factory: "0x619fb6C12c36b57a8bAb05e98F42C43745DCf69f",
|
|
32
32
|
blockExplorer: "https://megaeth-testnet-v2.blockscout.com"
|
|
33
|
+
},
|
|
34
|
+
// Mega Eth Mainnet
|
|
35
|
+
4326: {
|
|
36
|
+
chainId: 4326,
|
|
37
|
+
name: "Mega Eth Mainnet",
|
|
38
|
+
factoryAddress: "0xb2f6344Cb0Ee13e027759166770198f4d8B3106d",
|
|
39
|
+
wethAddress: "0x4200000000000000000000000000000000000006",
|
|
40
|
+
nonfungiblePositionManager: "0x2b781C57e6358f64864Ff8EC464a03Fdaf9974bA",
|
|
41
|
+
uniswapV3Factory: "0x68b34591f662508076927803c567Cc8006988a09",
|
|
42
|
+
blockExplorer: "https://megaeth.blockscout.com/"
|
|
33
43
|
}
|
|
34
44
|
};
|
|
35
45
|
function getChainConfig(chainId) {
|
|
@@ -761,12 +771,13 @@ var ManiaSDK = class _ManiaSDK {
|
|
|
761
771
|
async create(params) {
|
|
762
772
|
const wallet = this.getConnectedWallet();
|
|
763
773
|
const hash = await wallet.writeContract({
|
|
764
|
-
chain:
|
|
765
|
-
account:
|
|
774
|
+
chain: wallet.chain,
|
|
775
|
+
account: wallet.account,
|
|
766
776
|
address: this.factoryAddress,
|
|
767
777
|
abi: MANIA_FACTORY_ABI,
|
|
768
778
|
functionName: "create",
|
|
769
|
-
args: [params.name, params.symbol, params.uri, params.creator]
|
|
779
|
+
args: [params.name, params.symbol, params.uri, params.creator],
|
|
780
|
+
gas: 300000000n
|
|
770
781
|
});
|
|
771
782
|
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
772
783
|
const logs = parseEventLogs({
|
|
@@ -790,13 +801,14 @@ var ManiaSDK = class _ManiaSDK {
|
|
|
790
801
|
async createAndBuy(params) {
|
|
791
802
|
const wallet = this.getConnectedWallet();
|
|
792
803
|
const hash = await wallet.writeContract({
|
|
793
|
-
chain:
|
|
794
|
-
account:
|
|
804
|
+
chain: wallet.chain,
|
|
805
|
+
account: wallet.account,
|
|
795
806
|
address: this.factoryAddress,
|
|
796
807
|
abi: MANIA_FACTORY_ABI,
|
|
797
808
|
functionName: "createAndBuy",
|
|
798
809
|
args: [params.name, params.symbol, params.uri, params.creator, params.minTokensOut],
|
|
799
|
-
value: params.buyAmountEth
|
|
810
|
+
value: params.buyAmountEth,
|
|
811
|
+
gas: 300000000n
|
|
800
812
|
});
|
|
801
813
|
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
802
814
|
const logs = parseEventLogs({
|
|
@@ -821,13 +833,14 @@ var ManiaSDK = class _ManiaSDK {
|
|
|
821
833
|
const wallet = this.getConnectedWallet();
|
|
822
834
|
const recipient = params.recipient ?? wallet.account.address;
|
|
823
835
|
const hash = await wallet.writeContract({
|
|
824
|
-
chain:
|
|
825
|
-
account:
|
|
836
|
+
chain: wallet.chain,
|
|
837
|
+
account: wallet.account,
|
|
826
838
|
address: this.factoryAddress,
|
|
827
839
|
abi: MANIA_FACTORY_ABI,
|
|
828
840
|
functionName: "buy",
|
|
829
841
|
args: [params.token, params.minTokensOut, recipient],
|
|
830
|
-
value: params.amountEth
|
|
842
|
+
value: params.amountEth,
|
|
843
|
+
gas: 300000000n
|
|
831
844
|
});
|
|
832
845
|
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
833
846
|
return {
|
|
@@ -860,12 +873,13 @@ var ManiaSDK = class _ManiaSDK {
|
|
|
860
873
|
async sell(params) {
|
|
861
874
|
const wallet = this.getConnectedWallet();
|
|
862
875
|
const hash = await wallet.writeContract({
|
|
863
|
-
chain:
|
|
864
|
-
account:
|
|
876
|
+
chain: wallet.chain,
|
|
877
|
+
account: wallet.account,
|
|
865
878
|
address: this.factoryAddress,
|
|
866
879
|
abi: MANIA_FACTORY_ABI,
|
|
867
880
|
functionName: "sell",
|
|
868
|
-
args: [params.token, params.amountTokens, params.minEthOut]
|
|
881
|
+
args: [params.token, params.amountTokens, params.minEthOut],
|
|
882
|
+
gas: 300000000n
|
|
869
883
|
});
|
|
870
884
|
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
871
885
|
return {
|
|
@@ -899,13 +913,14 @@ var ManiaSDK = class _ManiaSDK {
|
|
|
899
913
|
const wallet = this.getConnectedWallet();
|
|
900
914
|
const globalState = await this.getGlobalState();
|
|
901
915
|
const hash = await wallet.writeContract({
|
|
902
|
-
chain:
|
|
903
|
-
account:
|
|
916
|
+
chain: wallet.chain,
|
|
917
|
+
account: wallet.account,
|
|
904
918
|
address: this.factoryAddress,
|
|
905
919
|
abi: MANIA_FACTORY_ABI,
|
|
906
920
|
functionName: "migrate",
|
|
907
921
|
args: [params.token],
|
|
908
|
-
value: globalState.poolMigrationFee
|
|
922
|
+
value: globalState.poolMigrationFee,
|
|
923
|
+
gas: 300000000n
|
|
909
924
|
});
|
|
910
925
|
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
911
926
|
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.2",
|
|
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
|
+
}
|