Skip to content

Commit

Permalink
Merge pull request #123 from ethstorage/sgt-payment
Browse files Browse the repository at this point in the history
Support soul gas token payment for storage fee
  • Loading branch information
qzhodl authored Nov 27, 2024
2 parents 9c7fc37 + 782f953 commit 41dea49
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 1 deletion.
31 changes: 31 additions & 0 deletions contracts/EthStorageContractL2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ interface IL1Block {
function timestamp() external view returns (uint64);
}

/// @title ISoulGasToken
/// @notice Interface for the SoulGasToken contract.
interface ISoulGasToken {
function chargeFromOrigin(uint256 _amount) external returns (uint256);
}

/// @custom:proxied
/// @title EthStorageContractL2
/// @notice EthStorage contract that will be deployed on L2, and uses L1Block contract to mine.
Expand All @@ -36,6 +42,9 @@ contract EthStorageContractL2 is EthStorageContract2 {
/// @notice A slot to store both `blockLastUpdate` (left 224) and `blobsUpdated` (right 32)
uint256 internal updateState;

/// @notice The address of the soul gas token.
address public soulGasToken;

/// @notice Constructs the EthStorageContractL2 contract.
constructor(
Config memory _config,
Expand All @@ -47,6 +56,28 @@ contract EthStorageContractL2 is EthStorageContract2 {
UPDATE_LIMIT = _updateLimit;
}

/// @notice Set the soul gas token address for the contract.
function setSoulGasToken(address _soulGasToken) external onlyOwner {
soulGasToken = _soulGasToken;
}

/// @inheritdoc StorageContract
function _checkAppend(uint256 _batchSize) internal virtual override {
uint256 kvEntryCountPrev = kvEntryCount - _batchSize; // kvEntryCount already increased
uint256 totalPayment = _upfrontPaymentInBatch(kvEntryCountPrev, _batchSize);
uint256 sgtCharged = 0;
if (soulGasToken != address(0)) {
sgtCharged = ISoulGasToken(soulGasToken).chargeFromOrigin(totalPayment);
}
require(msg.value >= totalPayment - sgtCharged, "EthStorageContractL2: not enough batch payment");

uint256 shardId = kvEntryCount >> SHARD_ENTRY_BITS; // shard id after the batch
if (shardId > (kvEntryCountPrev >> SHARD_ENTRY_BITS)) {
// Open a new shard and mark the shard is ready to mine.
infos[shardId].lastMineTime = _blockTs();
}
}

/// @notice Get the current block number
function _blockNumber() internal view virtual override returns (uint256) {
return L1_BLOCK.number();
Expand Down
2 changes: 1 addition & 1 deletion contracts/StorageContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ abstract contract StorageContract is DecentralizedKV {
}

/// @notice Upfront payment for a batch insertion
function _upfrontPaymentInBatch(uint256 _kvEntryCount, uint256 _batchSize) private view returns (uint256) {
function _upfrontPaymentInBatch(uint256 _kvEntryCount, uint256 _batchSize) internal view returns (uint256) {
uint256 shardId = _kvEntryCount >> SHARD_ENTRY_BITS;
uint256 totalEntries = _kvEntryCount + _batchSize; // include the batch to be put
uint256 totalPayment = 0;
Expand Down
53 changes: 53 additions & 0 deletions contracts/test/EthStorageContractL2Test.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import "forge-std/Test.sol";
import "./TestEthStorageContractL2.sol";
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";

contract SoulGasToken {
function chargeFromOrigin(uint256 _amount) external pure returns (uint256) {
return _amount;
}
}

contract EthStorageContractL2Test is Test {
uint256 constant STORAGE_COST = 0;
uint256 constant SHARD_SIZE_BITS = 19;
Expand Down Expand Up @@ -76,4 +82,51 @@ contract EthStorageContractL2Test is Test {
vm.expectRevert("EthStorageContractL2: exceeds update rate limit");
storageContract.putBlobs(keys, blobIdxs, lengths);
}

function testSGTPayment() public {
TestEthStorageContractL2 imp = new TestEthStorageContractL2(
StorageContract.Config(MAX_KV_SIZE, SHARD_SIZE_BITS, 2, 0, 0, 0),
block.timestamp,
1500000000000000,
0,
UPDATE_LIMIT
);
bytes memory data = abi.encodeWithSelector(
storageContract.initialize.selector, 0, PREPAID_AMOUNT, 0, address(0x1), address(0x1)
);
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(address(imp), owner, data);

TestEthStorageContractL2 l2Contract = TestEthStorageContractL2(address(proxy));

uint256 size = 6;
bytes32[] memory hashes = new bytes32[](size);
bytes32[] memory keys = new bytes32[](size);
uint256[] memory blobIdxs = new uint256[](size);
uint256[] memory lengths = new uint256[](size);
for (uint256 i = 0; i < size; i++) {
keys[i] = bytes32(uint256(i));
hashes[i] = bytes32(uint256((i + 1) << 64));
blobIdxs[i] = i;
lengths[i] = 10 + i * 10;
}
vm.blobhashes(hashes);

vm.expectRevert("EthStorageContractL2: not enough batch payment");
l2Contract.putBlobs{value: 1500000000000000 * 5}(keys, blobIdxs, lengths);

l2Contract.putBlobs{value: 1500000000000000 * 6}(keys, blobIdxs, lengths);

SoulGasToken sgt = new SoulGasToken();
vm.prank(owner);
l2Contract.setSoulGasToken(address(sgt));

for (uint256 i = 0; i < size; i++) {
keys[i] = bytes32(uint256(i + 6));
hashes[i] = bytes32(uint256((i + 1) << 64));
blobIdxs[i] = i;
lengths[i] = 10 + i * 10;
}
vm.blobhashes(hashes);
l2Contract.putBlobs(keys, blobIdxs, lengths);
}
}

0 comments on commit 41dea49

Please sign in to comment.