编译器
0.8.17+commit.8df45f5f
文件 1 的 8:AddressAliasHelper.sol
pragma solidity ^0.8.0;
library AddressAliasHelper {
uint160 internal constant OFFSET = uint160(0x1111000000000000000000000000000000001111);
function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
unchecked {
l2Address = address(uint160(l1Address) + OFFSET);
}
}
function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) {
unchecked {
l1Address = address(uint160(l2Address) - OFFSET);
}
}
}
文件 2 的 8:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 3 的 8:IBridge.sol
pragma solidity >=0.6.9 <0.9.0;
interface IBridge {
event MessageDelivered(
uint256 indexed messageIndex,
bytes32 indexed beforeInboxAcc,
address inbox,
uint8 kind,
address sender,
bytes32 messageDataHash
);
event BridgeCallTriggered(
address indexed outbox,
address indexed destAddr,
uint256 amount,
bytes data
);
event InboxToggle(address indexed inbox, bool enabled);
event OutboxToggle(address indexed outbox, bool enabled);
function deliverMessageToInbox(
uint8 kind,
address sender,
bytes32 messageDataHash
) external payable returns (uint256);
function executeCall(
address destAddr,
uint256 amount,
bytes calldata data
) external returns (bool success, bytes memory returnData);
function setInbox(address inbox, bool enabled) external;
function setOutbox(address inbox, bool enabled) external;
function activeOutbox() external view returns (address);
function allowedInboxes(address inbox) external view returns (bool);
function allowedOutboxes(address outbox) external view returns (bool);
function inboxAccs(uint256 index) external view returns (bytes32);
function messageCount() external view returns (uint256);
}
文件 4 的 8:IInbox.sol
pragma solidity >=0.6.9 <0.9.0;
import "./IBridge.sol";
import "./IMessageProvider.sol";
interface IInbox is IMessageProvider {
function sendL2Message(bytes calldata messageData) external returns (uint256);
function sendUnsignedTransaction(
uint256 maxGas,
uint256 gasPriceBid,
uint256 nonce,
address destAddr,
uint256 amount,
bytes calldata data
) external returns (uint256);
function sendContractTransaction(
uint256 maxGas,
uint256 gasPriceBid,
address destAddr,
uint256 amount,
bytes calldata data
) external returns (uint256);
function sendL1FundedUnsignedTransaction(
uint256 maxGas,
uint256 gasPriceBid,
uint256 nonce,
address destAddr,
bytes calldata data
) external payable returns (uint256);
function sendL1FundedContractTransaction(
uint256 maxGas,
uint256 gasPriceBid,
address destAddr,
bytes calldata data
) external payable returns (uint256);
function createRetryableTicket(
address destAddr,
uint256 arbTxCallValue,
uint256 maxSubmissionCost,
address submissionRefundAddress,
address valueRefundAddress,
uint256 maxGas,
uint256 gasPriceBid,
bytes calldata data
) external payable returns (uint256);
function unsafeCreateRetryableTicket(
address destAddr,
uint256 arbTxCallValue,
uint256 maxSubmissionCost,
address submissionRefundAddress,
address valueRefundAddress,
uint256 maxGas,
uint256 gasPriceBid,
bytes calldata data
) external payable returns (uint256);
function depositEth(uint256 maxSubmissionCost) external payable returns (uint256);
function bridge() external view returns (IBridge);
function pauseCreateRetryables() external;
function unpauseCreateRetryables() external;
function startRewriteAddress() external;
function stopRewriteAddress() external;
}
文件 5 的 8:IMessageProvider.sol
pragma solidity >=0.6.9 <0.9.0;
interface IMessageProvider {
event InboxMessageDelivered(uint256 indexed messageNum, bytes data);
event InboxMessageDeliveredFromOrigin(uint256 indexed messageNum);
}
文件 6 的 8:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 7 的 8:TicketsRedeemL1.sol
pragma solidity ^0.8.17;
import "arb-bridge-eth/contracts/bridge/interfaces/IInbox.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./TicketsRedeemL2.sol";
interface IERC1155Burnable {
function mint(
address account,
uint256 id,
uint256 amount,
bytes memory data
) external;
function burnBatch(
address account,
uint256[] memory ids,
uint256[] memory values
) external;
}
contract TicketsRedeemL1 is Ownable {
IERC1155Burnable public ferryTickets;
IERC1155Burnable public goldenBadges;
IInbox public inbox;
address public l2Target;
event TicketsRedeemInitiated(
uint indexed ticket,
address indexed wallet,
uint[] ids,
uint[] amounts
);
constructor(address _ferryTickets, address _goldenBadges, address _inbox) {
ferryTickets = IERC1155Burnable(_ferryTickets);
goldenBadges = IERC1155Burnable(_goldenBadges);
inbox = IInbox(_inbox);
}
function updateL2Target(address _l2Target) external onlyOwner {
l2Target = _l2Target;
}
function _random() internal view returns (uint) {
return
uint(
keccak256(
abi.encodePacked(
block.difficulty,
block.timestamp,
msg.sender,
gasleft()
)
)
) % 100;
}
function redeemTickets(
uint[] calldata ids,
uint[] calldata amounts,
uint maxSubmissionCost,
uint maxGas,
uint gasPriceBid
) public payable returns (uint) {
ferryTickets.burnBatch(msg.sender, ids, amounts);
bytes memory data = abi.encodeWithSelector(
TicketsRedeemL2.redeemTickets.selector,
msg.sender,
ids,
amounts
);
uint ticket = inbox.createRetryableTicket{value: msg.value}(
l2Target,
0,
maxSubmissionCost,
msg.sender,
msg.sender,
maxGas,
gasPriceBid,
data
);
uint badges = 0;
for (uint i = 0; i < ids.length; i++) {
if (ids[i] == 1) {
badges += amounts[i];
} else if (ids[i] == 2) {
for (uint j = 0; j < amounts[i]; j++) {
if (_random() < 10) {
badges += 1;
}
}
}
}
if (badges > 0) {
goldenBadges.mint(msg.sender, 1, badges, "");
}
emit TicketsRedeemInitiated(ticket, msg.sender, ids, amounts);
return ticket;
}
}
文件 8 的 8:TicketsRedeemL2.sol
pragma solidity ^0.8.17;
import "@arbitrum/nitro-contracts/src/libraries/AddressAliasHelper.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
interface BeastContract {
function safeMint(address to) external returns (uint256);
}
contract TicketsRedeemL2 is Ownable {
address public l1Target;
BeastContract public originBeasts;
BeastContract public beasts;
enum BeastCollection {
OriginBeast,
StarterBeast
}
event BeastRedeemed(
uint indexed tokenId,
BeastCollection indexed collection,
bool fromGoldenTicket
);
event TicketsRedeemed(address indexed from, uint[] ids, uint[] amounts);
constructor(address _l1Target, address _originBeasts, address _beasts) {
l1Target = _l1Target;
originBeasts = BeastContract(_originBeasts);
beasts = BeastContract(_beasts);
}
function updateL1Target(address _l1Target) external onlyOwner {
l1Target = _l1Target;
}
function _random() internal view returns (uint) {
return
uint(
keccak256(
abi.encodePacked(
block.difficulty,
block.timestamp,
msg.sender,
gasleft()
)
)
) % 100;
}
function redeemTickets(
address from,
uint[] calldata ids,
uint[] calldata amounts
) public {
require(
msg.sender == AddressAliasHelper.applyL1ToL2Alias(l1Target),
"Can only be called by L1 contract"
);
require(ids.length == amounts.length, "Invalid input length");
for (uint i = 0; i < ids.length; i++) {
uint256 tokenId = ids[i];
uint256 amount = amounts[i];
for (uint j = 0; j < amount; j++) {
if (tokenId == 1) {
uint originId = originBeasts.safeMint(from);
uint starterId = beasts.safeMint(from);
emit BeastRedeemed(
originId,
BeastCollection.OriginBeast,
true
);
emit BeastRedeemed(
starterId,
BeastCollection.StarterBeast,
true
);
} else if (tokenId == 2) {
uint originId = originBeasts.safeMint(from);
emit BeastRedeemed(
originId,
BeastCollection.OriginBeast,
false
);
if (_random() < 10) {
uint starterId = beasts.safeMint(from);
emit BeastRedeemed(
starterId,
BeastCollection.StarterBeast,
false
);
}
}
}
}
emit TicketsRedeemed(from, ids, amounts);
}
}
{
"compilationTarget": {
"contracts/TicketsRedeemL1.sol": "TicketsRedeemL1"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_ferryTickets","type":"address"},{"internalType":"address","name":"_goldenBadges","type":"address"},{"internalType":"address","name":"_inbox","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"ticket","type":"uint256"},{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"TicketsRedeemInitiated","type":"event"},{"inputs":[],"name":"ferryTickets","outputs":[{"internalType":"contract IERC1155Burnable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"goldenBadges","outputs":[{"internalType":"contract IERC1155Burnable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"inbox","outputs":[{"internalType":"contract IInbox","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l2Target","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256","name":"maxSubmissionCost","type":"uint256"},{"internalType":"uint256","name":"maxGas","type":"uint256"},{"internalType":"uint256","name":"gasPriceBid","type":"uint256"}],"name":"redeemTickets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_l2Target","type":"address"}],"name":"updateL2Target","outputs":[],"stateMutability":"nonpayable","type":"function"}]