Contrat
Contrat
0x34f5ee11ce…133d13de6d
0x34f5ee11ceef80d89ad461e817f4d1133d13de6d
Solde WTG
0 WTG
≈ 0,00 FCFA (@ 20,17 FCFA/WTG)
Avoirs en tokens
0 token
≈ 0,00 FCFA
Plus d'infos
Transactions envoyées1
Dernière activitéil y a 23 h
Première activitéil y a 23 h
Financé par
—
Code du contrat
✓ VérifiéWintgVerifyingPaymaster · Solidity v0.8.20+commit.a1b79de6 · optimiseur activé (200 runs) · licence MIT
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import { PackedUserOperation, IEntryPoint, IPaymaster, packValidationData } from "./interfaces.sol";
interface IERC20 {
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function balanceOf(address) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
}
abstract contract BasePaymaster is IPaymaster {
IEntryPoint public immutable entryPoint;
address public owner;
modifier onlyEntryPoint() {
require(msg.sender == address(entryPoint), "Paymaster: not EntryPoint");
_;
}
modifier onlyOwner() {
require(msg.sender == owner, "Paymaster: not owner");
_;
}
constructor(address entryPoint_, address owner_) {
entryPoint = IEntryPoint(entryPoint_);
owner = owner_;
}
function deposit() external payable {
entryPoint.depositTo{ value: msg.value }(address(this));
}
function getDeposit() external view returns (uint256) {
return entryPoint.balanceOf(address(this));
}
function withdrawTo(address payable to, uint256 amount) external onlyOwner {
entryPoint.withdrawTo(to, amount);
}
receive() external payable {
entryPoint.depositTo{ value: msg.value }(address(this));
}
}
contract WintgVerifyingPaymaster is BasePaymaster {
using ECDSA for bytes32;
address public verifyingSigner;
event SignerUpdated(address indexed signer);
constructor(address entryPoint_, address owner_, address signer_) BasePaymaster(entryPoint_, owner_) {
verifyingSigner = signer_;
}
function setSigner(address s) external onlyOwner {
verifyingSigner = s;
emit SignerUpdated(s);
}
function getHash(PackedUserOperation calldata op, uint48 validUntil, uint48 validAfter)
public
view
returns (bytes32)
{
return keccak256(
abi.encode(
op.sender,
op.nonce,
keccak256(op.initCode),
keccak256(op.callData),
op.accountGasLimits,
op.preVerificationGas,
op.gasFees,
block.chainid,
address(this),
validUntil,
validAfter
)
);
}
function validatePaymasterUserOp(PackedUserOperation calldata op, bytes32, uint256)
external
view
override
onlyEntryPoint
returns (bytes memory context, uint256 validationData)
{
bytes calldata pd = op.paymasterAndData;
uint48 validUntil = uint48(bytes6(pd[52:58]));
uint48 validAfter = uint48(bytes6(pd[58:64]));
bytes calldata sig = pd[64:];
bytes32 h = MessageHashUtils.toEthSignedMessageHash(getHash(op, validUntil, validAfter));
bool ok = ECDSA.recover(h, sig) == verifyingSigner;
return ("", packValidationData(!ok, validUntil, validAfter));
}
function postOp(PostOpMode, bytes calldata, uint256, uint256) external override onlyEntryPoint {
}
}
contract WintgTokenPaymaster is BasePaymaster {
mapping(address => uint256) public tokenPerWtg;
address public treasury;
event TokenSet(address indexed token, uint256 perWtg);
event GasPaidInToken(address indexed user, address indexed token, uint256 tokenAmount, uint256 wtgGasCost);
constructor(address entryPoint_, address owner_, address treasury_) BasePaymaster(entryPoint_, owner_) {
treasury = treasury_;
}
function setToken(address token, uint256 perWtg) external onlyOwner {
tokenPerWtg[token] = perWtg;
emit TokenSet(token, perWtg);
}
function setTreasury(address t) external onlyOwner {
treasury = t;
}
function _tokenAmount(address token, uint256 wtgCost) internal view returns (uint256) {
return (wtgCost * tokenPerWtg[token]) / 1e18;
}
function validatePaymasterUserOp(PackedUserOperation calldata op, bytes32, uint256 maxCost)
external
view
override
onlyEntryPoint
returns (bytes memory context, uint256 validationData)
{
address token = address(bytes20(op.paymasterAndData[52:72]));
require(tokenPerWtg[token] > 0, "TokenPaymaster: token not supported");
uint256 maxTokenCost = _tokenAmount(token, maxCost);
require(IERC20(token).balanceOf(op.sender) >= maxTokenCost, "TokenPaymaster: token balance");
require(IERC20(token).allowance(op.sender, address(this)) >= maxTokenCost, "TokenPaymaster: token allowance");
return (abi.encode(op.sender, token), 0);
}
function postOp(PostOpMode, bytes calldata context, uint256 actualGasCost, uint256)
external
override
onlyEntryPoint
{
(address user, address token) = abi.decode(context, (address, address));
uint256 tokenAmount = _tokenAmount(token, actualGasCost);
if (tokenAmount > 0) {
require(IERC20(token).transferFrom(user, treasury, tokenAmount), "TokenPaymaster: charge failed");
emit GasPaidInToken(user, token, tokenAmount, actualGasCost);
}
}
}
[
{
"type": "constructor",
"inputs": [
{
"name": "entryPoint_",
"type": "address",
"internalType": "address"
},
{
"name": "owner_",
"type": "address",
"internalType": "address"
},
{
"name": "signer_",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "nonpayable"
},
{
"type": "receive",
"stateMutability": "payable"
},
{
"type": "function",
"name": "deposit",
"inputs": [],
"outputs": [],
"stateMutability": "payable"
},
{
"type": "function",
"name": "entryPoint",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "contract IEntryPoint"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "getDeposit",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "getHash",
"inputs": [
{
"name": "op",
"type": "tuple",
"internalType": "struct PackedUserOperation",
"components": [
{
"name": "sender",
"type": "address",
"internalType": "address"
},
{
"name": "nonce",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "initCode",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "callData",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "accountGasLimits",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "preVerificationGas",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "gasFees",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "paymasterAndData",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "signature",
"type": "bytes",
"internalType": "bytes"
}
]
},
{
"name": "validUntil",
"type": "uint48",
"internalType": "uint48"
},
{
"name": "validAfter",
"type": "uint48",
"internalType": "uint48"
}
],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "owner",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "postOp",
"inputs": [
{
"name": "",
"type": "uint8",
"internalType": "enum IPaymaster.PostOpMode"
},
{
"name": "",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "setSigner",
"inputs": [
{
"name": "s",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "validatePaymasterUserOp",
"inputs": [
{
"name": "op",
"type": "tuple",
"internalType": "struct PackedUserOperation",
"components": [
{
"name": "sender",
"type": "address",
"internalType": "address"
},
{
"name": "nonce",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "initCode",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "callData",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "accountGasLimits",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "preVerificationGas",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "gasFees",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "paymasterAndData",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "signature",
"type": "bytes",
"internalType": "bytes"
}
]
},
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "context",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "validationData",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "verifyingSigner",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "withdrawTo",
"inputs": [
{
"name": "to",
"type": "address",
"internalType": "address payable"
},
{
"name": "amount",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "event",
"name": "SignerUpdated",
"inputs": [
{
"name": "signer",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"type": "error",
"name": "ECDSAInvalidSignature",
"inputs": []
},
{
"type": "error",
"name": "ECDSAInvalidSignatureLength",
"inputs": [
{
"name": "length",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"type": "error",
"name": "ECDSAInvalidSignatureS",
"inputs": [
{
"name": "s",
"type": "bytes32",
"internalType": "bytes32"
}
]
}
]
0x60406080815260049081361015610027575b5050361561001d575f80fd5b61002561061d565b005b5f91823560e01c918263205c28781461052e57826323d9ac9b1461050557826352b7512c146103475782635829c5f5146102db5782636c19e7831461026d5782637c627b21146101e35782638da5cb5b146101bb578263b0d691fe14610173578263c399ec88146100bd57505063d0e30db0146100a45780610011565b806003193601126100ba576100b761061d565b80f35b80fd5b9091503461015d578260031936011261015d5780516370a0823160e01b815230928101929092526020826024817f000000000000000000000000a8e7668576dc0b6490dba71fd53965b6ea4b913e6001600160a01b03165afa91821561016957839261012e575b6020838351908152f35b9091506020813d8211610161575b81610149602093836105fb565b8101031261015d576020925051905f610124565b8280fd5b3d915061013c565b81513d85823e3d90fd5b8390346101b757816003193601126101b757517f000000000000000000000000a8e7668576dc0b6490dba71fd53965b6ea4b913e6001600160a01b03168152602090f35b5080fd5b8390346101b757816003193601126101b757905490516001600160a01b039091168152602090f35b8382346101b75760803660031901126101b7576003813510156101b7576024359067ffffffffffffffff9081831161026957366023840112156102695782013590811161015d57369101602401116100ba576100b7337f000000000000000000000000a8e7668576dc0b6490dba71fd53965b6ea4b913e6001600160a01b03161461081e565b8380fd5b8382346101b75760203660031901126101b757356001600160a01b038181169182900361015d576102a2908354163314610690565b600180546001600160a01b031916821790557f5553331329228fbd4123164423717a4a7539f6dfa1c3279a923b98fd681a6c738280a280f35b91503461015d57600319926060368501126100ba5781359367ffffffffffffffff85116101b7576101209085360301126100ba575065ffffffffffff60243581811681036103435760443591821682036103435760209461033c930161074c565b9051908152f35b5f80fd5b8390346101b75760609060031993828536011261026957803567ffffffffffffffff9586821161050157610120828401918336030112610501576001600160a01b03916103c59060e4906103be337f000000000000000000000000a8e7668576dc0b6490dba71fd53965b6ea4b913e87161461081e565b01826106d3565b929083603a116104fd576034810135938087116104f9578161043961043f92610400603a61044897960135978860d01c908a60d01c9061074c565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000008d52601c52603c8c20923691603f1901908b01610706565b9061086a565b90939193610932565b60015481169116146104f2576001905b845197602094858a019182118a8310176104df5750855286885284519685885288518096890152805b8681106104cc575085880187015260ff9190911660309290921c65ffffffffffff60a01b16919091176001600160d01b03199091161790840152601f01601f19168201829003019150f35b8981018601518982018901528501610481565b604190634e487b7160e01b5f525260245ffd5b8590610458565b8880fd5b8780fd5b8580fd5b8390346101b757816003193601126101b75760015490516001600160a01b039091168152602090f35b9091503461015d578060031936011261015d578282356001600160a01b038181169182900361015d57610565818454163314610690565b7f000000000000000000000000a8e7668576dc0b6490dba71fd53965b6ea4b913e1693843b1561015d57604490838551968794859363040b850f60e31b855284015260243560248401525af19081156105ca57506105c1575080f35b6100b7906105d3565b513d84823e3d90fd5b67ffffffffffffffff81116105e757604052565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff8211176105e757604052565b7f000000000000000000000000a8e7668576dc0b6490dba71fd53965b6ea4b913e6001600160a01b0316803b15610343575f6024916040519283809263b760faf960e01b825230600483015234905af180156106855761067a5750565b610683906105d3565b565b6040513d5f823e3d90fd5b1561069757565b60405162461bcd60e51b81526020600482015260146024820152732830bcb6b0b9ba32b91d103737ba1037bbb732b960611b6044820152606490fd5b903590601e1981360301821215610343570180359067ffffffffffffffff82116103435760200191813603831361034357565b92919267ffffffffffffffff82116105e75760405191610730601f8201601f1916602001846105fb565b829481845281830111610343578281602093845f960137010152565b80356001600160a01b038116939192908490036103435760c061077c61077560408601866106d3565b3691610706565b602081519101209361079461077560608301836106d3565b6020815191012060405195602087019788526020830135604088015260608701526080860152608081013560a086015260a081013582860152013560e0840152466101008401523061012084015265ffffffffffff8091166101408401526101609116818301528152610180810181811067ffffffffffffffff8211176105e75760405251902090565b1561082557565b60405162461bcd60e51b815260206004820152601960248201527f5061796d61737465723a206e6f7420456e747279506f696e74000000000000006044820152606490fd5b815191906041830361089a576108939250602082015190606060408401519301515f1a906108a4565b9192909190565b50505f9160029190565b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841161092757926020929160ff6080956040519485521684840152604083015260608201525f92839182805260015afa1561091b5780516001600160a01b0381161561091257918190565b50809160019190565b604051903d90823e3d90fd5b5050505f9160039190565b60048110156109a15780610944575050565b6001810361095e5760405163f645eedf60e01b8152600490fd5b6002810361097f5760405163fce698f760e01b815260048101839052602490fd5b6003146109895750565b602490604051906335e2f38360e21b82526004820152fd5b634e487b7160e01b5f52602160045260245ffdfea2646970667358221220b35e6911456dc7c78e7f8d5384f25222228a381f6eaeea7af44e5e7099227fa564736f6c63430008140033
Aucun transfert de token.