Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
cmp20test
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
explorer-for-cmp20
cmp20test
Commits
e7dfd829
Commit
e7dfd829
authored
Feb 11, 2026
by
Developer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
update revert contract
parent
4c71963d
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
457 additions
and
11 deletions
+457
-11
Distribution.sol
contracts/Distribution.sol
+135
-0
revert.sol
contracts/revert.sol
+67
-0
deploy_distribute.js
scripts/deploy_distribute.js
+78
-0
distribute_erc20_all.js
scripts/distribute_erc20_all.js
+87
-0
token.js
scripts/token.js
+90
-11
No files found.
contracts/Distribution.sol
0 → 100644
View file @
e7dfd829
// filepath: /Users/luxq/work/wuban/testcontract/contracts/Distributor.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @dev Minimal ERC20 interface for token transfers
interface IERC20 {
function transfer(address to, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
/// @title Distributor - generate 700 pseudo-random addresses and distribute ETH to them
contract Distributor {
address[] public recipients;
uint256 public constant RECIPIENT_COUNT = 700;
/// @notice Construct the contract and generate RECIPIENT_COUNT addresses from a seed
/// @param seed A user provided seed to influence address generation (can be 0)
constructor(bytes32 seed) {
// generate deterministic pseudo-random addresses using keccak256 of seed, block timestamp and index
// Note: these are not real externally-owned accounts with known private keys — they are just 20-byte addresses
// and may be inaccessible if nobody controls the corresponding private key. Use for testing/stress only.
recipients = new address[](RECIPIENT_COUNT);
for (uint256 i = 0; i < RECIPIENT_COUNT; ++i) {
// mix seed with block.timestamp and sender to get some variability
bytes32 h = keccak256(abi.encodePacked(seed, block.timestamp, msg.sender, i));
// take the lower 160 bits as an address
recipients[i] = address(uint160(uint256(h)));
}
}
/// @notice Get the number of recipients
function recipientsCount() external pure returns (uint256) {
return RECIPIENT_COUNT;
}
/// @notice Distribute msg.value equally to all recipients.
/// @dev This will attempt RECIPIENT_COUNT transfers in a single transaction and may run out of gas.
/// It's provided for convenience, but prefer `distributeRange` with smaller ranges for production use.
function distributeAll() external payable {
uint256 total = msg.value;
require(total > 0, "No ETH provided");
uint256 per = total / RECIPIENT_COUNT;
require(per > 0, "Amount too small for equal distribution");
for (uint256 i = 0; i < RECIPIENT_COUNT; ++i) {
// use call to forward gas and avoid 2300 stipend issues
(bool success, ) = recipients[i].call{value: per}("");
// do not revert on single failure to maximize distribution; failed transfers keep funds in contract
if (!success) {
// continue; failed amount stays in contract
}
}
// refund any leftover (due to integer division or failed sends) back to sender
uint256 leftover = address(this).balance;
if (leftover > 0) {
payable(msg.sender).transfer(leftover);
}
}
/// @notice Distribute `amountPer` wei to each recipient in the half-open range [start, end)
/// @param start The starting index (inclusive)
/// @param end The end index (exclusive)
/// @param amountPer Amount in wei to send to each recipient in the range
function distributeRange(uint256 start, uint256 end, uint256 amountPer) external payable {
require(start < end && end <= RECIPIENT_COUNT, "Invalid range");
uint256 count = end - start;
uint256 required = count * amountPer;
require(msg.value >= required, "Insufficient ETH provided");
for (uint256 i = start; i < end; ++i) {
(bool success, ) = recipients[i].call{value: amountPer}("");
if (!success) {
// on failure, don't revert — leave failed amount in contract
}
}
// refund any extra wei back to sender
uint256 leftover = address(this).balance;
if (leftover > 0) {
payable(msg.sender).transfer(leftover);
}
}
/// @notice Return a copy of recipients (careful: large array)
function getRecipients() external view returns (address[] memory) {
return recipients;
}
/// @notice Distribute ERC20 tokens equally to all recipients from caller's balance.
/// @dev Caller must approve this contract to spend at least totalAmount tokens before calling.
/// This will attempt RECIPIENT_COUNT transfers in a single transaction and may run out of gas.
/// @param token The ERC20 token contract address
/// @param totalAmount The total amount of tokens to distribute
function distributeERC20All(address token, uint256 totalAmount) external {
require(totalAmount > 0, "No tokens provided");
uint256 per = totalAmount / RECIPIENT_COUNT;
require(per > 0, "Amount too small for equal distribution");
IERC20 erc20 = IERC20(token);
for (uint256 i = 0; i < RECIPIENT_COUNT; ++i) {
// transfer from caller to recipient
bool success = erc20.transferFrom(msg.sender, recipients[i], per);
if (!success) {
// continue on failure to maximize distribution
}
}
}
/// @notice Distribute ERC20 tokens to recipients in the half-open range [start, end)
/// @dev Caller must approve this contract to spend the required tokens before calling.
/// @param token The ERC20 token contract address
/// @param start The starting index (inclusive)
/// @param end The end index (exclusive)
/// @param amountPer Amount of tokens to send to each recipient in the range
function distributeERC20Range(address token, uint256 start, uint256 end, uint256 amountPer) external {
require(start < end && end <= RECIPIENT_COUNT, "Invalid range");
require(amountPer > 0, "Amount must be greater than 0");
IERC20 erc20 = IERC20(token);
for (uint256 i = start; i < end; ++i) {
// transfer from caller to recipient
bool success = erc20.transferFrom(msg.sender, recipients[i], amountPer);
if (!success) {
// continue on failure to maximize distribution
}
}
}
// Allow the contract to receive ETH
receive() external payable {}
}
contracts/revert.sol
View file @
e7dfd829
...
@@ -25,5 +25,72 @@ contract RevertContract {
...
@@ -25,5 +25,72 @@ contract RevertContract {
require(success, "Call failed");
require(success, "Call failed");
return success;
return success;
}
}
// --- New helper functions that call another contract which may revert ---
// Call a target's willRevertRequire() and let a revert bubble up (will revert this call too)
function callTargetAndBubble(address payable _target) public payable {
// typed call to external contract - if target reverts, this call will revert (bubble)
emit log("callTargetAndBubble: calling target, will bubble if it reverts");
RevertTarget(_target).willRevertRequire{value: msg.value}();
emit log("callTargetAndBubble: target call succeeded, this will be reached only if no revert");
}
// Call a target with try/catch and handle the revert without bubbling
function callTargetTryCatch(address payable _target) public payable returns (bool) {
emit log("callTargetTryCatch: calling target with try/catch, will handle revert");
try RevertTarget(_target).willRevertRequire{value: msg.value}() {
emit log("callTargetTryCatch: success");
return true;
} catch Error(string memory reason) {
// This is executed in case of revert with a reason string
emit log(reason);
return false;
} catch (bytes memory /*lowLevelData*/) {
// This is executed in case of a revert without a reason (or other failures)
emit log("callTargetTryCatch: unknown revert");
return false;
}
}
// Low-level call example: capture success flag and do not revert
function callTargetLowLevel(address payable _target) public payable returns (bool success, bytes memory data) {
emit log("callTargetLowLevel: calling target with low-level call, will handle success/failure");
(success, data) = _target.call{value: msg.value}(abi.encodeWithSignature("willRevertRequire()"));
if (!success) {
emit log("callTargetLowLevel: call failed, handled");
} else {
emit log("callTargetLowLevel: call succeeded");
}
}
receive() external payable {}
}
// Second-level contract that intentionally reverts in different ways
contract RevertTarget {
event logtarget(string message);
// revert via require (provides a reason string)
function willRevertRequire() external payable {
emit logtarget("RevertTarget: willRevertRequire called, about to revert");
require(false, "RevertTarget: require failed");
emit logtarget("RevertTarget: willRevertRequire - this will never be reached");
}
// explicit revert with message
function willRevertExplicit() external payable {
emit logtarget("RevertTarget: willRevertExplicit called, about to revert");
revert("RevertTarget: explicit revert");
emit logtarget("RevertTarget: willRevertExplicit - this will never be reached");
}
// panic via assert (produces a Panic(uint256))
function willPanic() external payable {
emit logtarget("RevertTarget: willPanic called, about to panic");
assert(false);
emit logtarget("RevertTarget: willPanic - this will never be reached");
}
// accept ether so tests can send value
receive() external payable {}
receive() external payable {}
}
}
\ No newline at end of file
scripts/deploy_distribute.js
0 → 100644
View file @
e7dfd829
// javascript
// scripts/deploy_distribute.js
// Deploy Distributor and call distributeAll to send ETH to all generated recipients
const
hre
=
require
(
"
hardhat
"
);
const
{
ethers
}
=
hre
;
async
function
main
()
{
const
[
deployer
]
=
await
ethers
.
getSigners
();
console
.
log
(
"
Using deployer:
"
,
deployer
.
address
);
const
Distributor
=
await
ethers
.
getContractFactory
(
"
Distributor
"
);
// Use a simple compatible bytes32 seed: keccak256("seed123") via ethers.utils.id
const
seed
=
"
0x626c756500000000000000000000000000000000000000000000000000000000
"
;
// print current block number.
const
blockNumber
=
await
ethers
.
provider
.
getBlockNumber
();
console
.
log
(
"
Current block number:
"
,
blockNumber
);
// get balance for deployer
const
deployerBalance
=
await
ethers
.
provider
.
getBalance
(
deployer
.
address
);
console
.
log
(
"
Deployer balance:
"
,
ethers
.
formatEther
(
deployerBalance
),
"
ETH
"
);
console
.
log
(
"
Deploying Distributor...
"
);
const
distributor
=
await
Distributor
.
deploy
(
seed
);
await
distributor
.
waitForDeployment
();
// <-- use waitForDeployment() in ethers v6
const
contractAddress
=
await
distributor
.
getAddress
();
console
.
log
(
"
Distributor deployed to:
"
,
contractAddress
);
// fetch recipients (beware: large array)
let
recipients
=
[];
try
{
recipients
=
await
distributor
.
getRecipients
();
}
catch
(
err
)
{
// some networks may limit returning large arrays; fall back to calling recipientsCount and reading a few
const
count
=
await
distributor
.
recipientsCount
();
for
(
let
i
=
0
;
i
<
Math
.
min
(
5
,
count
);
i
++
)
{
const
r
=
await
distributor
.
recipients
(
i
);
recipients
.
push
(
r
);
}
}
console
.
log
(
"
Recipients generated (cached):
"
,
recipients
.
length
);
console
.
log
(
"
First 5 recipients:
"
);
for
(
let
i
=
0
;
i
<
Math
.
min
(
5
,
recipients
.
length
);
i
++
)
{
console
.
log
(
i
,
recipients
[
i
]);
}
// show contract balance before
const
provider
=
ethers
.
provider
;
let
bal
=
await
provider
.
getBalance
(
contractAddress
);
console
.
log
(
"
Contract balance before:
"
,
ethers
.
formatEther
(
bal
));
// Send 1 ETH to distribute among 700 recipients
const
amountToSend
=
ethers
.
parseEther
(
"
1.0
"
);
console
.
log
(
`Calling distributeAll with
${
ethers
.
formatEther
(
amountToSend
)}
ETH...`
);
const
tx
=
await
distributor
.
connect
(
deployer
).
distributeAll
({
value
:
amountToSend
,
gasLimit
:
82
_000_000
});
const
receipt
=
await
tx
.
wait
();
console
.
log
(
"
distributeAll tx hash:
"
,
receipt
);
// show contract balance after
bal
=
await
provider
.
getBalance
(
contractAddress
);
console
.
log
(
"
Contract balance after:
"
,
ethers
.
formatEther
(
bal
));
// Show balances of first 5 recipients
console
.
log
(
"
Balances of first 5 recipients after distribution:
"
);
for
(
let
i
=
0
;
i
<
Math
.
min
(
5
,
recipients
.
length
);
i
++
)
{
const
b
=
await
provider
.
getBalance
(
recipients
[
i
]);
console
.
log
(
i
,
recipients
[
i
],
ethers
.
formatEther
(
b
));
}
console
.
log
(
"
Done.
"
);
}
main
().
catch
((
error
)
=>
{
console
.
error
(
error
);
process
.
exitCode
=
1
;
});
scripts/distribute_erc20_all.js
0 → 100644
View file @
e7dfd829
// filepath: /Users/luxq/work/wuban/testcontract/scripts/distribute_erc20_all.js
// Deploy Distributor and PublicToken, approve Distributor to spend tokens, and call distributeERC20All
const
hre
=
require
(
"
hardhat
"
);
const
{
ethers
}
=
hre
;
async
function
main
()
{
const
[
deployer
]
=
await
ethers
.
getSigners
();
console
.
log
(
"
Using deployer:
"
,
deployer
.
address
);
// Deploy Distributor (same pattern as deploy_distribute.js)
const
Distributor
=
await
ethers
.
getContractFactory
(
"
Distributor
"
);
const
seed
=
"
0x626c756500000000000000000000000000000000000000000000000000000000
"
;
console
.
log
(
"
Deploying Distributor...
"
);
const
distributor
=
await
Distributor
.
deploy
(
seed
);
await
distributor
.
waitForDeployment
();
const
distributorAddr
=
await
distributor
.
getAddress
();
console
.
log
(
"
Distributor deployed to:
"
,
distributorAddr
);
// Deploy PublicToken
const
Token
=
await
ethers
.
getContractFactory
(
"
PublicToken
"
);
console
.
log
(
"
Deploying PublicToken...
"
);
const
token
=
await
Token
.
deploy
();
await
token
.
waitForDeployment
();
const
tokenAddr
=
await
token
.
getAddress
();
console
.
log
(
"
PublicToken deployed to:
"
,
tokenAddr
);
// Determine decimals and prepare totalAmount to distribute
const
decimals
=
await
token
.
decimals
();
// choose an amount to distribute: 1000 tokens (in token units)
const
totalAmount
=
ethers
.
parseUnits
(
"
1000
"
,
decimals
);
console
.
log
(
`Will distribute totalAmount =
${
totalAmount
.
toString
()}
(parsed with decimals =
${
decimals
}
)`
);
// Mint tokens to deployer so there are tokens to distribute
console
.
log
(
"
Minting tokens to deployer...
"
);
const
mintTx
=
await
token
.
mint
(
deployer
.
address
,
totalAmount
);
console
.
log
(
"
Mint tx:
"
,
mintTx
.
hash
);
await
mintTx
.
wait
();
// Approve Distributor to spend tokens on behalf of deployer
console
.
log
(
"
Approving distributor to spend tokens...
"
);
const
approveTx
=
await
token
.
approve
(
distributorAddr
,
totalAmount
);
console
.
log
(
"
Approve tx:
"
,
approveTx
.
hash
);
await
approveTx
.
wait
();
// Fetch recipients (try full array, fallback to reading a few indices)
let
recipients
=
[];
try
{
recipients
=
await
distributor
.
getRecipients
();
}
catch
(
err
)
{
console
.
log
(
"
getRecipients() failed, falling back to reading indices:
"
,
err
.
message
||
err
);
const
count
=
await
distributor
.
recipientsCount
();
for
(
let
i
=
0
;
i
<
Math
.
min
(
5
,
count
);
i
++
)
{
const
r
=
await
distributor
.
recipients
(
i
);
recipients
.
push
(
r
);
}
}
console
.
log
(
"
Recipients count (cached):
"
,
recipients
.
length
);
console
.
log
(
"
First 5 recipients:
"
);
for
(
let
i
=
0
;
i
<
Math
.
min
(
5
,
recipients
.
length
);
i
++
)
{
console
.
log
(
i
,
recipients
[
i
]);
}
// Call distributeERC20All
console
.
log
(
"
Calling distributeERC20All...
"
);
const
tx
=
await
distributor
.
connect
(
deployer
).
distributeERC20All
(
tokenAddr
,
totalAmount
,
{
gasLimit
:
82
_000_000
});
const
receipt
=
await
tx
.
wait
();
console
.
log
(
"
distributeERC20All tx receipt:
"
,
receipt
.
transactionHash
);
// Compute per-recipient amount and show balances for first 5 recipients
const
RECIPIENT_COUNT
=
await
distributor
.
recipientsCount
();
const
per
=
totalAmount
/
RECIPIENT_COUNT
;
console
.
log
(
`Per recipient amount (integer division):
${
per
.
toString
()}
`
);
console
.
log
(
"
Token balances of first 5 recipients:
"
);
for
(
let
i
=
0
;
i
<
Math
.
min
(
5
,
recipients
.
length
);
i
++
)
{
const
b
=
await
token
.
balanceOf
(
recipients
[
i
]);
console
.
log
(
i
,
recipients
[
i
],
b
.
toString
());
}
console
.
log
(
"
Done.
"
);
}
main
().
catch
((
error
)
=>
{
console
.
error
(
error
);
process
.
exitCode
=
1
;
});
scripts/token.js
View file @
e7dfd829
const
{
ContractFactory
}
=
require
(
"
ethers
"
);
const
hre
=
require
(
"
hardhat
"
);
const
hre
=
require
(
"
hardhat
"
);
async
function
getTokenInfo
(
contract
)
{
async
function
getTokenInfo
(
contract
)
{
...
@@ -11,26 +10,106 @@ async function getTokenInfo(contract) {
...
@@ -11,26 +10,106 @@ async function getTokenInfo(contract) {
console
.
log
(
"
Total Supply:
"
,
totalSupply
.
toString
());
console
.
log
(
"
Total Supply:
"
,
totalSupply
.
toString
());
}
}
// Define the script
// Helper: fetch and print recent Transfer and Approval events for a contract
async
function
main
()
{
async
function
getLogs
(
contract
,
lookbackBlocks
=
1000
)
{
const
provider
=
hre
.
ethers
.
provider
;
const
latest
=
await
provider
.
getBlockNumber
();
const
fromBlock
=
Math
.
max
(
0
,
latest
-
lookbackBlocks
);
console
.
log
(
`Fetching logs from block
${
fromBlock
}
to
${
latest
}
...`
);
const
transferFilter
=
contract
.
filters
.
Transfer
();
const
approvalFilter
=
contract
.
filters
.
Approval
();
const
transferEvents
=
await
contract
.
queryFilter
(
transferFilter
,
fromBlock
,
latest
);
const
approvalEvents
=
await
contract
.
queryFilter
(
approvalFilter
,
fromBlock
,
latest
);
const
printEvent
=
(
e
)
=>
{
const
args
=
e
.
args
?
Array
.
from
(
e
.
args
).
map
(
a
=>
(
a
&&
a
.
toString
?
a
.
toString
()
:
a
))
:
[];
return
`[block:
${
e
.
blockNumber
}
tx:
${
e
.
transactionHash
}
]
${
e
.
event
}
(
${
args
.
join
(
'
,
'
)}
)`
;
};
console
.
log
(
`Found
${
transferEvents
.
length
}
Transfer event(s):`
);
transferEvents
.
forEach
(
e
=>
console
.
log
(
printEvent
(
e
)));
console
.
log
(
`Found
${
approvalEvents
.
length
}
Approval event(s):`
);
approvalEvents
.
forEach
(
e
=>
console
.
log
(
printEvent
(
e
)));
}
// Small helper to print an address balance
async
function
printBalance
(
contract
,
address
,
label
)
{
const
bal
=
await
contract
.
balanceOf
(
address
);
console
.
log
(
`
${
label
}
balance (
${
address
}
):`
,
bal
.
toString
());
}
// Deploy the contract
// Main script
const
factory
=
await
hre
.
ethers
.
getContractFactory
(
async
function
main
()
{
"
PublicToken
"
// Deploy the PublicToken contract
);
const
factory
=
await
hre
.
ethers
.
getContractFactory
(
"
PublicToken
"
);
// const contract = await factory.deploy({ value: hre.ethers.parseEther("0.0008") });
const
contract
=
await
factory
.
deploy
();
const
contract
=
await
factory
.
deploy
();
await
contract
.
waitForDeployment
();
await
contract
.
waitForDeployment
();
const
depAddr
=
await
contract
.
getAddress
();
const
depAddr
=
await
contract
.
getAddress
();
console
.
log
(
"
Contract deployed at:
"
,
depAddr
);
console
.
log
(
"
Contract deployed at:
"
,
depAddr
);
// Basic info
await
getTokenInfo
(
contract
);
await
getTokenInfo
(
contract
);
// const minttx = await contract.mint('0x88395111AB1586a4030dAC62a183542762929bbC', 10000);
// console.log("mint tx:", minttx.hash);
// Show any existing logs (if any)
await
getLogs
(
contract
);
// Mint tokens and wait for mining so logs are available
const
recipient
=
'
0x88395111AB1586a4030dAC62a183542762929bbC
'
;
const
amount
=
10000
;
const
minttx
=
await
contract
.
mint
(
recipient
,
amount
);
console
.
log
(
"
mint to user:
"
,
recipient
,
"
tx:
"
,
minttx
.
hash
);
await
minttx
.
wait
();
// Fetch logs again to show the mint Transfer event
await
getLogs
(
contract
);
const
signers
=
await
hre
.
ethers
.
getSigners
();
// Define a spender (use signer[1] if present) and a transfer recipient (signer[2] or a fixed address)
const
deployer
=
signers
[
0
];
const
spender
=
'
0x1000000000000000000000000000000000000001
'
;
const
transferTo
=
'
0x2000000000000000000000000000000000000002
'
;
const
approvalAmount
=
5000
;
try
{
const
approveTx
=
await
contract
.
approve
(
spender
,
approvalAmount
);
console
.
log
(
'
approve tx:
'
,
approveTx
.
hash
);
await
approveTx
.
wait
();
}
catch
(
err
)
{
console
.
log
(
'
approve failed:
'
,
err
.
message
||
err
);
}
// Print allowance and balances
try
{
const
allowance
=
await
contract
.
allowance
(
deployer
,
spender
);
console
.
log
(
`Allowance of spender
${
spender
}
by owner :`
,
deployer
,
allowance
.
toString
());
}
catch
(
err
)
{
console
.
log
(
'
Could not fetch allowance:
'
,
err
.
message
||
err
);
}
// Now perform the transfer from deployer
const
transferAmount
=
300
;
try
{
const
transferTx
=
await
contract
.
transfer
(
transferTo
,
transferAmount
);
console
.
log
(
'
transfer tx:
'
,
transferTx
.
hash
);
await
transferTx
.
wait
();
}
catch
(
err
)
{
console
.
log
(
'
transfer failed:
'
,
err
.
message
||
err
);
}
// Print balances after operations
// await printBalance(contract, recipient, 'Recipient (final)');
// await printBalance(contract, transferTo, 'TransferTo (final)');
// Fetch logs to show the approve and transfer events
await
getLogs
(
contract
);
}
}
// Run
the script
// Run
main
().
catch
((
error
)
=>
{
main
().
catch
((
error
)
=>
{
console
.
error
(
"
Error:
"
,
error
);
console
.
error
(
"
Error:
"
,
error
);
process
.
exitCode
=
1
;
});
});
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment