@sentrix-labs/canonical-contracts 1.1.0
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/CHANGELOG.md +70 -0
- package/LICENSE +21 -0
- package/NOTICE +19 -0
- package/README.md +150 -0
- package/contracts/CoinBlastCurve.sol +369 -0
- package/contracts/CoinBlastFactory.sol +76 -0
- package/contracts/MerkleAirdrop.sol +142 -0
- package/contracts/Multicall3.sol +125 -0
- package/contracts/SentrixSafe.sol +252 -0
- package/contracts/StrategicReserveTimelock.sol +104 -0
- package/contracts/TokenFactory.sol +106 -0
- package/contracts/WSRX.sol +95 -0
- package/contracts/interfaces/ISentrixSafe.sol +42 -0
- package/contracts/interfaces/ITokenFactory.sol +27 -0
- package/contracts/interfaces/IWSRX.sol +30 -0
- package/contracts/mocks/MockERC20.sol +60 -0
- package/contracts/mocks/MockSRX.sol +28 -0
- package/deployments/7119.json +75 -0
- package/deployments/7120.json +51 -0
- package/deployments/README.md +70 -0
- package/deployments/abi/FactoryToken.json +1 -0
- package/deployments/abi/Multicall3.json +1 -0
- package/deployments/abi/SentrixSafe.json +1 -0
- package/deployments/abi/TokenFactory.json +1 -0
- package/deployments/abi/WSRX.json +1 -0
- package/deployments/abi/index.js +11 -0
- package/dist/generated.d.ts +3181 -0
- package/dist/generated.d.ts.map +1 -0
- package/dist/index.cjs +1429 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1415 -0
- package/dist/index.js.map +1 -0
- package/package.json +74 -0
- package/src/generated.ts +1441 -0
- package/src/index.ts +41 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity 0.8.24;
|
|
3
|
+
|
|
4
|
+
import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol";
|
|
5
|
+
|
|
6
|
+
/// @title StrategicReserveTimelock
|
|
7
|
+
/// @author Sentrix Labs
|
|
8
|
+
/// @notice Holds the Strategic Reserve (10,500,000 SRX) under enforced
|
|
9
|
+
/// timelock. Spends require: SentrixSafe schedules a call → wait
|
|
10
|
+
/// minDelay → SentrixSafe executes.
|
|
11
|
+
/// @dev Thin wrapper over OpenZeppelin's audited TimelockController v5.6.0
|
|
12
|
+
/// (battle-tested in Compound governance, holds $billions). No
|
|
13
|
+
/// custom logic — just constructor that hardcodes Sentrix
|
|
14
|
+
/// configuration:
|
|
15
|
+
/// - minDelay = 24 hours (86400 seconds)
|
|
16
|
+
/// - proposer = SentrixSafe (Authority signer required to schedule)
|
|
17
|
+
/// - executor = SentrixSafe (Authority signer required to execute
|
|
18
|
+
/// post-delay)
|
|
19
|
+
/// - canceller = SentrixSafe (can cancel pending ops)
|
|
20
|
+
/// - admin = address(0) — fully self-administered. Role changes
|
|
21
|
+
/// themselves go through the timelock.
|
|
22
|
+
///
|
|
23
|
+
/// Migration flow (one-time):
|
|
24
|
+
/// 1. Deploy this contract.
|
|
25
|
+
/// 2. Transfer Strategic Reserve EOA balance (10.5M SRX) to this
|
|
26
|
+
/// contract address.
|
|
27
|
+
/// 3. Reserve EOA private key is retired (zero out, never reuse).
|
|
28
|
+
///
|
|
29
|
+
/// Spend flow (recurring):
|
|
30
|
+
/// 1. SentrixSafe calls schedule(target, value, data, predecessor,
|
|
31
|
+
/// salt, delay) — operation queued.
|
|
32
|
+
/// 2. Wait at least minDelay (24h).
|
|
33
|
+
/// 3. SentrixSafe calls execute(target, value, data, predecessor,
|
|
34
|
+
/// salt) — operation runs.
|
|
35
|
+
/// 4. Optional: cancel(id) before delay elapses if mistake/coercion
|
|
36
|
+
/// detected.
|
|
37
|
+
///
|
|
38
|
+
/// Cancel flow (operator safety):
|
|
39
|
+
/// - 24-hour window between schedule + execute lets operator catch
|
|
40
|
+
/// wrong amounts, recipients, or proposed-under-coercion spends
|
|
41
|
+
/// and cancel before damage.
|
|
42
|
+
contract StrategicReserveTimelock is TimelockController {
|
|
43
|
+
/// @notice Construct the timelock with Sentrix's standard config.
|
|
44
|
+
/// @param sentrixSafe The SentrixSafe address (mainnet
|
|
45
|
+
/// 0x6272dC0C842F05542f9fF7B5443E93C0642a3b26 / testnet
|
|
46
|
+
/// 0xc9D7a61D7C2F428F6A055916488041fD00532110). Granted
|
|
47
|
+
/// proposer + executor + canceller roles.
|
|
48
|
+
constructor(address sentrixSafe)
|
|
49
|
+
TimelockController(
|
|
50
|
+
// minDelay: 24 hours. Operations cannot be scheduled with
|
|
51
|
+
// a shorter delay than this — and only the timelock itself
|
|
52
|
+
// (via timelocked proposal) can change the minDelay.
|
|
53
|
+
86400,
|
|
54
|
+
// proposers: SentrixSafe is the only address authorized to
|
|
55
|
+
// schedule new spend operations.
|
|
56
|
+
_singletonArray(sentrixSafe),
|
|
57
|
+
// executors: SentrixSafe is the only address authorized to
|
|
58
|
+
// execute scheduled operations after the delay elapses.
|
|
59
|
+
// We deliberately do NOT pass address(0) (which would let
|
|
60
|
+
// anyone execute) — this keeps the audit trail clean
|
|
61
|
+
// (every execution is via SentrixSafe execTransaction).
|
|
62
|
+
_singletonArray(sentrixSafe),
|
|
63
|
+
// admin: address(0) means no admin role. Role changes go
|
|
64
|
+
// through timelock itself (proposed, delayed, executed).
|
|
65
|
+
// This prevents anyone — including the deployer — from
|
|
66
|
+
// bypassing the timelock to grant new roles.
|
|
67
|
+
address(0)
|
|
68
|
+
)
|
|
69
|
+
{}
|
|
70
|
+
|
|
71
|
+
/// @dev Helper: build a single-element address array for the
|
|
72
|
+
/// TimelockController constructor.
|
|
73
|
+
function _singletonArray(address addr)
|
|
74
|
+
private
|
|
75
|
+
pure
|
|
76
|
+
returns (address[] memory)
|
|
77
|
+
{
|
|
78
|
+
address[] memory arr = new address[](1);
|
|
79
|
+
arr[0] = addr;
|
|
80
|
+
return arr;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Inherits from TimelockController:
|
|
84
|
+
// - schedule(target, value, data, predecessor, salt, delay)
|
|
85
|
+
// - scheduleBatch(targets, values, payloads, predecessor, salt, delay)
|
|
86
|
+
// - execute(target, value, data, predecessor, salt)
|
|
87
|
+
// - executeBatch(targets, values, payloads, predecessor, salt)
|
|
88
|
+
// - cancel(id)
|
|
89
|
+
// - getOperationState(id)
|
|
90
|
+
// - hasRole(role, account)
|
|
91
|
+
// - All standard AccessControl methods
|
|
92
|
+
// - receive() to accept native SRX
|
|
93
|
+
|
|
94
|
+
// No custom logic. All security properties come from OZ
|
|
95
|
+
// TimelockController v5.6.0:
|
|
96
|
+
// - Time-lock enforcement (operation cannot execute before
|
|
97
|
+
// readyTimestamp = scheduleTimestamp + delay)
|
|
98
|
+
// - Role-based access (only PROPOSER can schedule, only EXECUTOR
|
|
99
|
+
// can execute, only CANCELLER can cancel)
|
|
100
|
+
// - Self-administration (role changes go through timelock)
|
|
101
|
+
// - Reentrancy-safe (uses internal _execute)
|
|
102
|
+
// - Predecessor support (operations can require another op to be
|
|
103
|
+
// done first, for atomic multi-step flows)
|
|
104
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity 0.8.24;
|
|
3
|
+
|
|
4
|
+
/// @title Sentrix TokenFactory
|
|
5
|
+
/// @author Sentrix Labs
|
|
6
|
+
/// @notice Deploys standard ERC-20 tokens. Single entry point for builders
|
|
7
|
+
/// who want a fungible token without writing Solidity. Initial
|
|
8
|
+
/// supply is minted to the deployer (`msg.sender`).
|
|
9
|
+
/// @dev Tracks deployed-token-by-deployer to make discovery easy.
|
|
10
|
+
contract TokenFactory {
|
|
11
|
+
event TokenDeployed(address indexed token, address indexed owner, string name, string symbol, uint256 initialSupply);
|
|
12
|
+
|
|
13
|
+
/// @dev Caps name/symbol bytes to prevent gas-grief deploys with megabyte
|
|
14
|
+
/// strings. Standard ERC20 metadata fits comfortably under both caps.
|
|
15
|
+
uint256 public constant MAX_NAME_LENGTH = 64;
|
|
16
|
+
uint256 public constant MAX_SYMBOL_LENGTH = 16;
|
|
17
|
+
|
|
18
|
+
mapping(address => address[]) public deployedTokens;
|
|
19
|
+
|
|
20
|
+
function deployToken(string calldata name, string calldata symbol, uint256 initialSupply) external returns (address token) {
|
|
21
|
+
// Reject silly inputs early — a zero-supply or empty-name token is
|
|
22
|
+
// never useful and burns gas for everyone indexing the event log.
|
|
23
|
+
require(initialSupply > 0, "TokenFactory: ZERO_SUPPLY");
|
|
24
|
+
require(bytes(name).length > 0 && bytes(name).length <= MAX_NAME_LENGTH, "TokenFactory: BAD_NAME");
|
|
25
|
+
require(bytes(symbol).length > 0 && bytes(symbol).length <= MAX_SYMBOL_LENGTH, "TokenFactory: BAD_SYMBOL");
|
|
26
|
+
|
|
27
|
+
FactoryToken t = new FactoryToken(name, symbol, initialSupply, msg.sender);
|
|
28
|
+
token = address(t);
|
|
29
|
+
deployedTokens[msg.sender].push(token);
|
|
30
|
+
emit TokenDeployed(token, msg.sender, name, symbol, initialSupply);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function tokensOf(address owner) external view returns (address[] memory) {
|
|
34
|
+
return deployedTokens[owner];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function tokenCount(address owner) external view returns (uint256) {
|
|
38
|
+
return deployedTokens[owner].length;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// @notice Minimal ERC-20 deployed by the factory.
|
|
43
|
+
contract FactoryToken {
|
|
44
|
+
string public name;
|
|
45
|
+
string public symbol;
|
|
46
|
+
uint8 public constant decimals = 18;
|
|
47
|
+
uint256 public totalSupply;
|
|
48
|
+
|
|
49
|
+
mapping(address => uint256) public balanceOf;
|
|
50
|
+
mapping(address => mapping(address => uint256)) public allowance;
|
|
51
|
+
|
|
52
|
+
event Transfer(address indexed from, address indexed to, uint256 value);
|
|
53
|
+
event Approval(address indexed owner, address indexed spender, uint256 value);
|
|
54
|
+
|
|
55
|
+
constructor(string memory _name, string memory _symbol, uint256 _initialSupply, address _owner) {
|
|
56
|
+
// Defense-in-depth: TokenFactory always passes msg.sender so this
|
|
57
|
+
// can't trigger via the factory path, but a direct deploy with
|
|
58
|
+
// _owner=0 would trap the entire supply in the zero address. Reject.
|
|
59
|
+
require(_owner != address(0), "FactoryToken: OWNER_ZERO");
|
|
60
|
+
name = _name;
|
|
61
|
+
symbol = _symbol;
|
|
62
|
+
totalSupply = _initialSupply;
|
|
63
|
+
balanceOf[_owner] = _initialSupply;
|
|
64
|
+
emit Transfer(address(0), _owner, _initialSupply);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function transfer(address to, uint256 amount) external returns (bool) {
|
|
68
|
+
return _transfer(msg.sender, to, amount);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function approve(address spender, uint256 amount) external returns (bool) {
|
|
72
|
+
allowance[msg.sender][spender] = amount;
|
|
73
|
+
emit Approval(msg.sender, spender, amount);
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function transferFrom(address from, address to, uint256 amount) external returns (bool) {
|
|
78
|
+
if (from != msg.sender) {
|
|
79
|
+
uint256 allowed = allowance[from][msg.sender];
|
|
80
|
+
if (allowed != type(uint256).max) {
|
|
81
|
+
require(allowed >= amount, "FactoryToken: insufficient allowance");
|
|
82
|
+
uint256 remaining = allowed - amount;
|
|
83
|
+
allowance[from][msg.sender] = remaining;
|
|
84
|
+
// OZ-style: emit Approval on each spend so indexers tracking
|
|
85
|
+
// allowance state changes (e.g. CoinGecko / explorer ABI
|
|
86
|
+
// decoders) see the new value without re-reading storage.
|
|
87
|
+
emit Approval(from, msg.sender, remaining);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return _transfer(from, to, amount);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function _transfer(address from, address to, uint256 amount) internal returns (bool) {
|
|
94
|
+
// ERC-20 spec says transfers to address(0) are not standard burns —
|
|
95
|
+
// most token contracts revert. Be explicit so wallets that show a
|
|
96
|
+
// failed tx instead of an unexpected `Transfer(... 0x0 ...)` log.
|
|
97
|
+
require(to != address(0), "FactoryToken: TO_ZERO");
|
|
98
|
+
require(balanceOf[from] >= amount, "FactoryToken: insufficient balance");
|
|
99
|
+
unchecked {
|
|
100
|
+
balanceOf[from] -= amount;
|
|
101
|
+
balanceOf[to] += amount;
|
|
102
|
+
}
|
|
103
|
+
emit Transfer(from, to, amount);
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity 0.8.24;
|
|
3
|
+
|
|
4
|
+
/// @title Wrapped SRX
|
|
5
|
+
/// @author Sentrix Labs
|
|
6
|
+
/// @notice Wraps native SRX into an ERC-20 token (18 decimals at the EVM
|
|
7
|
+
/// boundary). 1 SRX = 10^10 wei due to native ledger using 8
|
|
8
|
+
/// decimals; conversion is handled by the EVM database adapter,
|
|
9
|
+
/// so `msg.value` here is already the wei amount and `wad` minted
|
|
10
|
+
/// equals `msg.value` 1:1.
|
|
11
|
+
/// @dev Standard wrapped-token pattern (mirror of WETH9). Self-contained,
|
|
12
|
+
/// no external imports - keeps the canonical-contracts repo
|
|
13
|
+
/// dependency-free. Compiles with solc 0.8.24, no SafeMath needed.
|
|
14
|
+
contract WSRX {
|
|
15
|
+
string public constant name = "Wrapped SRX";
|
|
16
|
+
string public constant symbol = "WSRX";
|
|
17
|
+
uint8 public constant decimals = 18;
|
|
18
|
+
|
|
19
|
+
uint256 public totalSupply;
|
|
20
|
+
|
|
21
|
+
mapping(address => uint256) public balanceOf;
|
|
22
|
+
mapping(address => mapping(address => uint256)) public allowance;
|
|
23
|
+
|
|
24
|
+
event Transfer(address indexed src, address indexed dst, uint256 wad);
|
|
25
|
+
event Approval(address indexed src, address indexed guy, uint256 wad);
|
|
26
|
+
event Deposit(address indexed dst, uint256 wad);
|
|
27
|
+
event Withdrawal(address indexed src, uint256 wad);
|
|
28
|
+
|
|
29
|
+
/// @notice Wrap native SRX 1:1 into WSRX by sending value to this contract.
|
|
30
|
+
receive() external payable {
|
|
31
|
+
deposit();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// @notice Wrap `msg.value` native SRX 1:1 into WSRX credited to caller.
|
|
35
|
+
function deposit() public payable {
|
|
36
|
+
balanceOf[msg.sender] += msg.value;
|
|
37
|
+
totalSupply += msg.value;
|
|
38
|
+
emit Deposit(msg.sender, msg.value);
|
|
39
|
+
emit Transfer(address(0), msg.sender, msg.value);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// @notice Burn `wad` WSRX and return native SRX to caller.
|
|
43
|
+
/// @param wad Amount in wei (18-decimal). Must not exceed caller's balance.
|
|
44
|
+
function withdraw(uint256 wad) external {
|
|
45
|
+
require(balanceOf[msg.sender] >= wad, "WSRX: insufficient balance");
|
|
46
|
+
balanceOf[msg.sender] -= wad;
|
|
47
|
+
totalSupply -= wad;
|
|
48
|
+
(bool ok, ) = msg.sender.call{value: wad}("");
|
|
49
|
+
require(ok, "WSRX: native transfer failed");
|
|
50
|
+
emit Withdrawal(msg.sender, wad);
|
|
51
|
+
emit Transfer(msg.sender, address(0), wad);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// @notice Approve `guy` to spend up to `wad` of caller's WSRX.
|
|
55
|
+
/// @param guy Spender.
|
|
56
|
+
/// @param wad Allowance in wei. Pass `type(uint256).max` for infinite allowance.
|
|
57
|
+
/// @return Always true on success (matches ERC-20 spec).
|
|
58
|
+
function approve(address guy, uint256 wad) external returns (bool) {
|
|
59
|
+
allowance[msg.sender][guy] = wad;
|
|
60
|
+
emit Approval(msg.sender, guy, wad);
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/// @notice Transfer `wad` WSRX to `dst`.
|
|
65
|
+
/// @param dst Recipient.
|
|
66
|
+
/// @param wad Amount in wei.
|
|
67
|
+
/// @return Always true on success.
|
|
68
|
+
function transfer(address dst, uint256 wad) external returns (bool) {
|
|
69
|
+
return _transferFrom(msg.sender, dst, wad);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/// @notice Transfer `wad` WSRX from `src` to `dst` using caller's allowance.
|
|
73
|
+
/// @param src Token holder.
|
|
74
|
+
/// @param dst Recipient.
|
|
75
|
+
/// @param wad Amount in wei.
|
|
76
|
+
/// @return Always true on success.
|
|
77
|
+
function transferFrom(address src, address dst, uint256 wad) external returns (bool) {
|
|
78
|
+
if (src != msg.sender) {
|
|
79
|
+
uint256 allowed = allowance[src][msg.sender];
|
|
80
|
+
if (allowed != type(uint256).max) {
|
|
81
|
+
require(allowed >= wad, "WSRX: insufficient allowance");
|
|
82
|
+
allowance[src][msg.sender] = allowed - wad;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return _transferFrom(src, dst, wad);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function _transferFrom(address src, address dst, uint256 wad) internal returns (bool) {
|
|
89
|
+
require(balanceOf[src] >= wad, "WSRX: insufficient balance");
|
|
90
|
+
balanceOf[src] -= wad;
|
|
91
|
+
balanceOf[dst] += wad;
|
|
92
|
+
emit Transfer(src, dst, wad);
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity 0.8.24;
|
|
3
|
+
|
|
4
|
+
/// @title ISentrixSafe
|
|
5
|
+
/// @author Sentrix Labs
|
|
6
|
+
/// @notice Minimal multi-signature wallet interface. Owners sign tx hashes
|
|
7
|
+
/// off-chain; on-chain `execTransaction` verifies threshold + nonce.
|
|
8
|
+
interface ISentrixSafe {
|
|
9
|
+
event ExecutionSuccess(bytes32 indexed txHash, uint256 nonce);
|
|
10
|
+
event ExecutionFailure(bytes32 indexed txHash, uint256 nonce);
|
|
11
|
+
event AddedOwner(address indexed owner);
|
|
12
|
+
event RemovedOwner(address indexed owner);
|
|
13
|
+
event ChangedThreshold(uint256 threshold);
|
|
14
|
+
|
|
15
|
+
function threshold() external view returns (uint256);
|
|
16
|
+
function nonce() external view returns (uint256);
|
|
17
|
+
function isOwner(address owner) external view returns (bool);
|
|
18
|
+
function getOwners() external view returns (address[] memory);
|
|
19
|
+
|
|
20
|
+
/// @notice Execute a tx with `threshold` signatures.
|
|
21
|
+
/// @param to Target address.
|
|
22
|
+
/// @param value Native value (wei) to send.
|
|
23
|
+
/// @param data Calldata.
|
|
24
|
+
/// @param operation 0 = call, 1 = delegatecall.
|
|
25
|
+
/// @param signatures Concatenated 65-byte ECDSA signatures, sorted by signer ascending.
|
|
26
|
+
/// @return success True if the execution succeeded.
|
|
27
|
+
function execTransaction(
|
|
28
|
+
address to,
|
|
29
|
+
uint256 value,
|
|
30
|
+
bytes calldata data,
|
|
31
|
+
uint256 operation,
|
|
32
|
+
bytes calldata signatures
|
|
33
|
+
) external returns (bool success);
|
|
34
|
+
|
|
35
|
+
function getTransactionHash(
|
|
36
|
+
address to,
|
|
37
|
+
uint256 value,
|
|
38
|
+
bytes calldata data,
|
|
39
|
+
uint256 operation,
|
|
40
|
+
uint256 _nonce
|
|
41
|
+
) external view returns (bytes32);
|
|
42
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity 0.8.24;
|
|
3
|
+
|
|
4
|
+
/// @title ITokenFactory
|
|
5
|
+
/// @author Sentrix Labs
|
|
6
|
+
/// @notice Deploys minimal ERC-20 tokens. One-call factory; emits TokenDeployed.
|
|
7
|
+
interface ITokenFactory {
|
|
8
|
+
event TokenDeployed(
|
|
9
|
+
address indexed token,
|
|
10
|
+
address indexed owner,
|
|
11
|
+
string name,
|
|
12
|
+
string symbol,
|
|
13
|
+
uint256 initialSupply
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
/// @notice Deploy a new ERC-20 with `initialSupply` minted to caller.
|
|
17
|
+
/// @return token Address of the new ERC-20 contract.
|
|
18
|
+
function deployToken(string calldata name, string calldata symbol, uint256 initialSupply)
|
|
19
|
+
external
|
|
20
|
+
returns (address token);
|
|
21
|
+
|
|
22
|
+
/// @notice Returns all tokens deployed by `owner`.
|
|
23
|
+
function tokensOf(address owner) external view returns (address[] memory);
|
|
24
|
+
|
|
25
|
+
/// @notice Number of tokens deployed by `owner`.
|
|
26
|
+
function tokenCount(address owner) external view returns (uint256);
|
|
27
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity 0.8.24;
|
|
3
|
+
|
|
4
|
+
/// @title IWSRX
|
|
5
|
+
/// @author Sentrix Labs
|
|
6
|
+
/// @notice Wrapped SRX interface — wrap/unwrap native SRX into a 1:1 ERC-20.
|
|
7
|
+
interface IWSRX {
|
|
8
|
+
event Transfer(address indexed src, address indexed dst, uint256 wad);
|
|
9
|
+
event Approval(address indexed src, address indexed guy, uint256 wad);
|
|
10
|
+
event Deposit(address indexed dst, uint256 wad);
|
|
11
|
+
event Withdrawal(address indexed src, uint256 wad);
|
|
12
|
+
|
|
13
|
+
function name() external view returns (string memory);
|
|
14
|
+
function symbol() external view returns (string memory);
|
|
15
|
+
function decimals() external view returns (uint8);
|
|
16
|
+
function totalSupply() external view returns (uint256);
|
|
17
|
+
function balanceOf(address owner) external view returns (uint256);
|
|
18
|
+
function allowance(address owner, address spender) external view returns (uint256);
|
|
19
|
+
|
|
20
|
+
/// @notice Wrap native SRX 1:1 into WSRX.
|
|
21
|
+
function deposit() external payable;
|
|
22
|
+
|
|
23
|
+
/// @notice Burn WSRX, return native SRX.
|
|
24
|
+
/// @param wad Amount in wei (18-dec).
|
|
25
|
+
function withdraw(uint256 wad) external;
|
|
26
|
+
|
|
27
|
+
function approve(address guy, uint256 wad) external returns (bool);
|
|
28
|
+
function transfer(address dst, uint256 wad) external returns (bool);
|
|
29
|
+
function transferFrom(address src, address dst, uint256 wad) external returns (bool);
|
|
30
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity 0.8.24;
|
|
3
|
+
|
|
4
|
+
/// @title MockERC20
|
|
5
|
+
/// @author Sentrix Labs
|
|
6
|
+
/// @notice Test-only ERC-20 with public mint. Use only in test/.
|
|
7
|
+
contract MockERC20 {
|
|
8
|
+
string public name;
|
|
9
|
+
string public symbol;
|
|
10
|
+
uint8 public immutable decimals;
|
|
11
|
+
uint256 public totalSupply;
|
|
12
|
+
|
|
13
|
+
mapping(address => uint256) public balanceOf;
|
|
14
|
+
mapping(address => mapping(address => uint256)) public allowance;
|
|
15
|
+
|
|
16
|
+
event Transfer(address indexed from, address indexed to, uint256 value);
|
|
17
|
+
event Approval(address indexed owner, address indexed spender, uint256 value);
|
|
18
|
+
|
|
19
|
+
constructor(string memory _name, string memory _symbol, uint8 _decimals) {
|
|
20
|
+
name = _name;
|
|
21
|
+
symbol = _symbol;
|
|
22
|
+
decimals = _decimals;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/// @notice Test helper: mint to any address. NEVER use in prod.
|
|
26
|
+
function mint(address to, uint256 amount) external {
|
|
27
|
+
balanceOf[to] += amount;
|
|
28
|
+
totalSupply += amount;
|
|
29
|
+
emit Transfer(address(0), to, amount);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function transfer(address to, uint256 amount) external returns (bool) {
|
|
33
|
+
return _transfer(msg.sender, to, amount);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function approve(address spender, uint256 amount) external returns (bool) {
|
|
37
|
+
allowance[msg.sender][spender] = amount;
|
|
38
|
+
emit Approval(msg.sender, spender, amount);
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function transferFrom(address from, address to, uint256 amount) external returns (bool) {
|
|
43
|
+
if (from != msg.sender) {
|
|
44
|
+
uint256 allowed = allowance[from][msg.sender];
|
|
45
|
+
if (allowed != type(uint256).max) {
|
|
46
|
+
require(allowed >= amount, "MockERC20: insufficient allowance");
|
|
47
|
+
allowance[from][msg.sender] = allowed - amount;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return _transfer(from, to, amount);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function _transfer(address from, address to, uint256 amount) internal returns (bool) {
|
|
54
|
+
require(balanceOf[from] >= amount, "MockERC20: insufficient balance");
|
|
55
|
+
balanceOf[from] -= amount;
|
|
56
|
+
balanceOf[to] += amount;
|
|
57
|
+
emit Transfer(from, to, amount);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity 0.8.24;
|
|
3
|
+
|
|
4
|
+
/// @title MockSRX
|
|
5
|
+
/// @author Sentrix Labs
|
|
6
|
+
/// @notice Test-only payable contract that simulates a native-SRX holder.
|
|
7
|
+
/// Lets tests send native value into and out of a known contract
|
|
8
|
+
/// without involving the real chain. Use only in test/.
|
|
9
|
+
contract MockSRX {
|
|
10
|
+
event Received(address indexed from, uint256 amount);
|
|
11
|
+
event Forwarded(address indexed to, uint256 amount);
|
|
12
|
+
|
|
13
|
+
receive() external payable {
|
|
14
|
+
emit Received(msg.sender, msg.value);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/// @notice Forward held native SRX to `to`. Test helper only.
|
|
18
|
+
function forward(address payable to, uint256 amount) external {
|
|
19
|
+
require(address(this).balance >= amount, "MockSRX: insufficient");
|
|
20
|
+
(bool ok, ) = to.call{value: amount}("");
|
|
21
|
+
require(ok, "MockSRX: forward failed");
|
|
22
|
+
emit Forwarded(to, amount);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function balance() external view returns (uint256) {
|
|
26
|
+
return address(this).balance;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_chainId": 7119,
|
|
3
|
+
"_network": "Sentrix Mainnet",
|
|
4
|
+
"_note": "Canonical contracts deployed via cast send --create with explicit --nonce per call (forge script forking blocked by Sentrix RPC strict-decode of sha3Uncles; mainnet mempool also enforces strict sequential nonce admission, so each broadcast waited for the prior to mine before sending). Deployer: 0x5acb04058fc4dfa258f29ce318282377cac176fd.",
|
|
5
|
+
"WSRX": {
|
|
6
|
+
"address": "0x4693b113e523A196d9579333c4ab8358e2656553",
|
|
7
|
+
"tx": "0xc5b5016338ba2de65ebb631374724bcc33db63b9a570e77d455d896b40f103fb",
|
|
8
|
+
"block": 716787,
|
|
9
|
+
"deployer": "0x5acb04058fc4dfa258f29ce318282377cac176fd",
|
|
10
|
+
"deployer_nonce": 0,
|
|
11
|
+
"deployed_at": "2026-04-27"
|
|
12
|
+
},
|
|
13
|
+
"Multicall3": {
|
|
14
|
+
"address": "0xFd4b34b5763f54a580a0d9f7997A2A993ef9ceE9",
|
|
15
|
+
"tx": "0x64633e100f952d845970590fda32786118cf5e6b29b56c281d8d1e4b8e889f0a",
|
|
16
|
+
"block": 717078,
|
|
17
|
+
"deployer": "0x5acb04058fc4dfa258f29ce318282377cac176fd",
|
|
18
|
+
"deployer_nonce": 1,
|
|
19
|
+
"deployed_at": "2026-04-27",
|
|
20
|
+
"_note": "Plain CREATE for v1; canonical CREATE2 address 0xcA11bde05977b3631167028862bE2a173976CA11 not used yet."
|
|
21
|
+
},
|
|
22
|
+
"TokenFactory": {
|
|
23
|
+
"address": "0xc753199b723649ab92c6db8A45F158921CFDEe49",
|
|
24
|
+
"tx": "0xfda6219d60219e223d049158bca734d77e475c0b6b0c02074beeba0c701be112",
|
|
25
|
+
"block": 717392,
|
|
26
|
+
"deployer": "0x5acb04058fc4dfa258f29ce318282377cac176fd",
|
|
27
|
+
"deployer_nonce": 2,
|
|
28
|
+
"deployed_at": "2026-04-27"
|
|
29
|
+
},
|
|
30
|
+
"SentrixSafe": {
|
|
31
|
+
"address": "0x6272dC0C842F05542f9fF7B5443E93C0642a3b26",
|
|
32
|
+
"tx": "0xc67fb31dd135051732a41530e26897fb7c10eaec1fc6cb9334b596073758cb0f",
|
|
33
|
+
"block": 717618,
|
|
34
|
+
"deployer": "0x5acb04058fc4dfa258f29ce318282377cac176fd",
|
|
35
|
+
"deployer_nonce": 3,
|
|
36
|
+
"deployed_at": "2026-04-27",
|
|
37
|
+
"owners": [
|
|
38
|
+
"0x5acb04058fc4dfa258f29ce318282377cac176fd"
|
|
39
|
+
],
|
|
40
|
+
"threshold": 1,
|
|
41
|
+
"_note": "Bootstrap-mode 1-of-1 with deployer as owner. Transfer to operator multisig owners post-launch via execTransaction(addOwner, removeOwner)."
|
|
42
|
+
},
|
|
43
|
+
"TokenFactory_v1_1_0": {
|
|
44
|
+
"address": "0x53C3838e18703c763564Bb983694CF117B33D366",
|
|
45
|
+
"tx": "0x74a32557c9455cff3892fbac30da3b77a2cf70808ff314638e0c09808dcb30fd",
|
|
46
|
+
"deployed_at": "2026-05-01",
|
|
47
|
+
"signer_label": "Founder v3 (one-shot deploy gas)",
|
|
48
|
+
"_changes_vs_v1_0": "input validation (ZERO_SUPPLY/BAD_NAME/BAD_SYMBOL); FactoryToken transfer-to-zero reverts (ERC-20 spec)",
|
|
49
|
+
"_replaces": "0xc753199b723649ab92c6db8A45F158921CFDEe49"
|
|
50
|
+
},
|
|
51
|
+
"CoinBlastCurve_CBLAST": {
|
|
52
|
+
"address": "0x7a2992af0d4979add076347666023d66d29276fc",
|
|
53
|
+
"token": "0xe1d502b93ce492cbde32a369d5393626e38d55a7",
|
|
54
|
+
"tx": "0x8a988bea192422d8a6bfd8b8b736ec463857f5c2c8fd82c2680e3451819e13af",
|
|
55
|
+
"block": 1161074,
|
|
56
|
+
"deployer": "0x5acb04058fc4dfa258f29ce318282377cac176fd",
|
|
57
|
+
"deployer_nonce": 6,
|
|
58
|
+
"deployed_at": "2026-05-01",
|
|
59
|
+
"params": {
|
|
60
|
+
"name": "CoinBlast Genesis",
|
|
61
|
+
"symbol": "CBLAST",
|
|
62
|
+
"curveSupply": "1000000000000000000000000000",
|
|
63
|
+
"basePriceNum": 1,
|
|
64
|
+
"basePriceDen": 10000,
|
|
65
|
+
"kNum": 1,
|
|
66
|
+
"kDen": 2,
|
|
67
|
+
"graduationSrxThreshold": "1000000000000000000000",
|
|
68
|
+
"feeBps": 100,
|
|
69
|
+
"feeRecipient": "0xeb70fdefd00fdb768dec06c478f450c351499f14",
|
|
70
|
+
"router": "0xAb67E171c0DE0Cd6dD6fE87E5E399C091F9c9dE8",
|
|
71
|
+
"wsrx": "0x4693b113e523A196d9579333c4ab8358e2656553"
|
|
72
|
+
},
|
|
73
|
+
"_note": "First on-chain CoinBlastCurve launch. P0 = 1e-4 SRX-wei per token-wei (= 0.0001 SRX per whole token), K = 0.5, graduation at 1000 SRX raised. Fees accrue to Ecosystem Fund. Curve address coincidentally equals testnet TokenFactory v1.0.0 — same deployer EOA, deterministic CREATE-from-nonce alignment."
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_chainId": 7120,
|
|
3
|
+
"_network": "Sentrix Testnet",
|
|
4
|
+
"_note": "Canonical contracts deployed via cast send --create (forge script forking blocked by Sentrix RPC strict-decode of sha3Uncles). Deployer: 0x5acb04058fc4dfa258f29ce318282377cac176fd.",
|
|
5
|
+
"WSRX": {
|
|
6
|
+
"address": "0x85d5E7694AF31C2Edd0a7e66b7c6c92C59fF949A",
|
|
7
|
+
"tx": "0xfcfd2e0c1b3b4e61a2166a35cbb780d8370e9d1d6c67f7137aeb66c52260e8b3",
|
|
8
|
+
"block": 723183,
|
|
9
|
+
"deployer": "0x5acb04058fc4dfa258f29ce318282377cac176fd",
|
|
10
|
+
"deployer_nonce": 4,
|
|
11
|
+
"deployed_at": "2026-04-27"
|
|
12
|
+
},
|
|
13
|
+
"Multicall3": {
|
|
14
|
+
"address": "0x7900826De548425c6BE56caEbD4760AB0155Cd54",
|
|
15
|
+
"tx": "0x1f8d6749f7ffdcbbaabfad21167d5133593944be6c59025c69c3f6543cb7f6c2",
|
|
16
|
+
"block": 723191,
|
|
17
|
+
"deployer": "0x5acb04058fc4dfa258f29ce318282377cac176fd",
|
|
18
|
+
"deployer_nonce": 5,
|
|
19
|
+
"deployed_at": "2026-04-27",
|
|
20
|
+
"_note": "Plain CREATE for v1; canonical CREATE2 address 0xcA11bde05977b3631167028862bE2a173976CA11 not used yet."
|
|
21
|
+
},
|
|
22
|
+
"TokenFactory": {
|
|
23
|
+
"address": "0x7A2992af0d4979aDD076347666023d66d29276Fc",
|
|
24
|
+
"tx": "0xe68e5553af080a97181e09279782873f884477759656e51c03f522c94cb9da47",
|
|
25
|
+
"block": 723195,
|
|
26
|
+
"deployer": "0x5acb04058fc4dfa258f29ce318282377cac176fd",
|
|
27
|
+
"deployer_nonce": 6,
|
|
28
|
+
"deployed_at": "2026-04-27"
|
|
29
|
+
},
|
|
30
|
+
"SentrixSafe": {
|
|
31
|
+
"address": "0xc9D7a61D7C2F428F6A055916488041fD00532110",
|
|
32
|
+
"tx": "0x514863050498a545fa627d696aa82cdd2558bc35470418d7f83af8f1c0a12176",
|
|
33
|
+
"block": 723511,
|
|
34
|
+
"deployer": "0x5acb04058fc4dfa258f29ce318282377cac176fd",
|
|
35
|
+
"deployer_nonce": 7,
|
|
36
|
+
"deployed_at": "2026-04-27",
|
|
37
|
+
"owners": [
|
|
38
|
+
"0x5acb04058fc4dfa258f29ce318282377cac176fd"
|
|
39
|
+
],
|
|
40
|
+
"threshold": 1,
|
|
41
|
+
"_note": "Bootstrap-mode 1-of-1 with deployer as owner. Transfer to operator multisig owners post-launch via execTransaction(addOwner, removeOwner)."
|
|
42
|
+
},
|
|
43
|
+
"TokenFactory_v1_1_0": {
|
|
44
|
+
"address": "0xaE2a8512f0de635F8E90069e2877098c9e0baEc7",
|
|
45
|
+
"tx": "0x119ac2ff65361a36b4a7720b39c8a8bd6d2b2d601fdae480f25c5e250cb3ea9e",
|
|
46
|
+
"deployed_at": "2026-05-01",
|
|
47
|
+
"signer_label": "testnet faucet (one-shot deploy gas)",
|
|
48
|
+
"_changes_vs_v1_0": "input validation (ZERO_SUPPLY/BAD_NAME/BAD_SYMBOL); FactoryToken transfer-to-zero reverts (ERC-20 spec)",
|
|
49
|
+
"_replaces": "0x7A2992af0d4979aDD076347666023d66d29276Fc"
|
|
50
|
+
}
|
|
51
|
+
}
|