Building a Storage Proof Powered Bridge
WIP!!!!
Last updated
const ethers = require('ethers');
function getSlot(mappingSlot, key) {
return ethers.utils.keccak256(
ethers.utils.defaultAbiCoder.encode(
['bytes32', 'uint256'],
[key, mappingSlot]
)
);
}const TRANSFERS_SLOT = 5; // obtained from storage layout
const key = '0x1234...'; // your specific key
const slot = getSlot(TRANSFERS_SLOT, key);
console.log('Calculated slot:', slot);async function requestStorageProof(chainId, contractAddress, slot, blockNumber, apiKey) {
const response = await fetch('https://api.herodotus.cloud/submit-batch-query', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify({
chainId,
contractAddress,
slots: [slot],
blockNumber
})
});
return await response.json();
}
// Usage
const proofRequest = await requestStorageProof(
1, // Ethereum mainnet
'0x1234...', // Your contract address
slot,
14000000, // Block number
'YOUR_API_KEY'
);
console.log('Proof request:', proofRequest);async function getProvenData(taskId, apiKey) {
const response = await fetch(`https://api.herodotus.cloud/batch-query-status/${taskId}`, {
headers: {
'Authorization': `Bearer ${apiKey}`
}
});
const proofData = await response.json();
if (proofData.status === 'DONE') {
const slotValue = proofData.slots[0].value;
console.log('Proven slot value:', slotValue);
return slotValue;
} else {
console.log('Proof not yet finalized');
return null;
}
}
// Usage
const provenValue = await getProvenData(proofRequest.taskId, 'YOUR_API_KEY');pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IFactsRegistry {
function verifyStorage(
address account,
uint256 blockNumber,
bytes32 slot,
bytes memory storageSlotTrieProof
) external view returns (bytes32 slotValue);
}
contract Bridge {
IFactsRegistry public factsRegistry;
IERC20 public token;
uint256 public sourceChainId;
address public sourceTokenContract;
constructor(address _factsRegistry, address _token, uint256 _sourceChainId, address _sourceTokenContract) {
factsRegistry = IFactsRegistry(_factsRegistry);
token = IERC20(_token);
sourceChainId = _sourceChainId;
sourceTokenContract = _sourceTokenContract;
}
function bridgeTransfer(
bytes32 transferId,
uint256 blockNumber,
bytes memory storageSlotTrieProof
) external {
bytes32 slot = keccak256(abi.encode(transferId, uint256(5))); // Assuming transfers mapping is at slot 5
bytes32 slotValue = factsRegistry.verifyStorage(
sourceTokenContract,
blockNumber,
slot,
storageSlotTrieProof
);
(address recipient, uint256 amount) = decodeSlotValue(slotValue);
require(recipient != address(0), "Invalid recipient");
require(amount > 0, "Invalid amount");
// Perform bridge logic
token.transfer(recipient, amount);
emit TransferBridged(transferId, recipient, amount);
}
function decodeSlotValue(bytes32 slotValue) internal pure returns (address recipient, uint256 amount) {
// Implement decoding logic based on your data structure
// This is a simplified example
recipient = address(uint160(uint256(slotValue)));
amount = uint256(slotValue) >> 160;
}
event TransferBridged(bytes32 indexed transferId, address indexed recipient, uint256 amount);
}