本文以事故为核心,聚焦 Redstone Oracles 的技术细节、失败原因、解决方案及预防措施,结构清晰,注重知识点讲解。
在 Scroll 网络(Layer 2,链 ID:534352)上,我尝试通过疑似 Loanshark 的借贷协议(loanshark)赎回抵押的 0.023162 ETH,但调用 CETH 合约(地址:0xF017f9CF11558d143E603d56Ec81E4E3B6d39D7F
)的 redeem
和 redeemUnderlying
方法失败。通过 Tenderly 工具分析,错误源于 Redstone Oracles 的 _extractByteSizeOfUnsignedMetadata
函数,提示交易 calldata 缺少或包含无效的 Redstone payload。最终,我通过 JavaScript 脚本结合 Redstone 的 evm-connector
库成功赎回资产。
本文以事故为核心,聚焦 Redstone Oracles 的技术细节、失败原因、解决方案及预防措施,结构清晰,注重知识点讲解。
我在 Scroll 网络上通过借贷协议抵押了 0.023162 ETH,试图赎回时,通过 Scrollscan 调用 CETH 合约的 redeem
和 redeemUnderlying
方法失败。Tenderly 分析显示错误发生在 Redstone Oracles 的 _extractByteSizeOfUnsignedMetadata
函数,提示 calldata 缺少有效 Redstone payload。最终,我通过脚本使用 Redstone 的 evm-connector
库成功赎回。
相关合约:
0xF017f9CF11558d143E603d56Ec81E4E3B6d39D7F
0xEFB0697700E5c489073a9BDF7EF94a2B2bc884a5
https://api.redstone.finance/prices?symbol=ETH&provider=redstone-rapid&limit=1
)报错。redeem
和 redeemUnderlying
,均失败。_extractByteSizeOfUnsignedMetadata
。Redstone Oracles 是一个去中心化预言机系统,用于将链下数据(如价格)注入智能合约。它通过在交易 calldata 末尾附加 payload 传递数据,适用于 DeFi 协议(如借贷、DEX),支持价格喂价、链下计算等。
Redstone payload 是附加在 calldata 末尾的数据包,包含:
0x000002ed57011e0000
),位于末尾。keccak256("ETH/USD")
。示例:
[...交易数据...][数据包][元数据][大小: 3字节][marker: 9字节]
数据包(ETH/USD):
[签名: 65字节][数据点数: 1][值大小: 32][ETH/USD ID][价格: 3000e8][时间戳]
_extractByteSizeOfUnsignedMetadata
函数详解function _extractByteSizeOfUnsignedMetadata() internal pure returns (uint256) {
bool hasValidRedstoneMarker;
assembly {
let calldataLast32Bytes := calldataload(sub(calldatasize(), 32))
hasValidRedstoneMarker := eq(REDSTONE_MARKER_MASK, and(calldataLast32Bytes, REDSTONE_MARKER_MASK))
}
if (!hasValidRedstoneMarker) revert CalldataMustHaveValidPayload();
uint24 unsignedMetadataByteSize;
if (41 > msg.data.length) revert CalldataOverOrUnderFlow();
assembly {
unsignedMetadataByteSize := calldataload(sub(calldatasize(), 41))
}
uint256 calldataNegativeOffset = unsignedMetadataByteSize + 3 + 9;
if (calldataNegativeOffset + 2 > msg.data.length) revert IncorrectUnsignedMetadataSize();
return calldataNegativeOffset;
}
元数据大小 + 3 + 9
。CalldataMustHaveValidPayload
:无 marker。CalldataOverOrUnderFlow
:calldata 过短。IncorrectUnsignedMetadataSize
:偏移无效。Comptroller
的 redeemAllowed
函数依赖 Redstone 价格数据(如 ETH/USD),用于:
redeem
或 redeemUnderlying
。Comptroller.redeemAllowed
,通过 ProxyConnector
转发 calldata。ProxyConnector
使用 CalldataExtractor
解析 payload,提取价格。redeem
时,未附加 Redstone payload,_extractByteSizeOfUnsignedMetadata
无法找到 marker,抛出 CalldataMustHaveValidPayload
。Comptroller
无法获取价格数据,赎回验证失败。SignerNotAuthorised
。DataPackageTimestampMustNotBeZero
或 DataPackageTimestampsMustBeEqual
。https://api.redstone.finance/prices
)暗示配置问题。evm-connector
生成包含 ETH/USD 价格的 payload。https://rpc.scroll.io
,链 ID:534352)。以下是成功赎回的脚本:
require('dotenv').config();
const ethers = require('ethers'); // 5.7.2
const { WrapperBuilder } = require('@redstone-finance/evm-connector'); // 0.7.3
const scrollRpcUrl = 'https://rpc.scroll.io';
const cethAddress = '0xF017f9CF11558d143E603d56Ec81E4E3B6d39D7F';
const cethAbi = ['function redeemUnderlying(uint redeemAmount) external returns (uint)'];
const redstoneConfig = {
dataServiceId: 'redstone-primary-prod',
uniqueSignersCount: 1,
dataPackagesIds: ['ETH']
};
async function executeRedeem() {
const provider = new ethers.providers.JsonRpcProvider(scrollRpcUrl);
const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
const cethContract = new ethers.Contract(cethAddress, cethAbi, signer);
const wrappedCethContract = WrapperBuilder.wrap(cethContract).usingDataService(redstoneConfig);
const tx = await wrappedCethContract.redeemUnderlying(ethers.utils.parseUnits('1', 18), { gasLimit: 1000000 });
const receipt = await tx.wait();
console.log('赎回成功! 交易哈希:', receipt.transactionHash);
}
executeRedeem().catch(console.error);
关键点:
ethers@5.7.2
和 evm-connector@0.7.3
确保兼容性。Comptroller
需求。Comptroller
的 redeemAllowed
需 Redstone 价格数据验证赎回。evm-connector
自动附加 payload,避免手动调用。ETH
)和数据服务(redstone-primary-prod
)正确,参考 Redstone 文档.事故原因:在 Scrollscan 手动调用 redeem
时,缺少 Redstone payload,导致 _extractByteSizeOfUnsignedMetadata
抛出错误。Comptroller
依赖价格数据验证赎回,无 payload 导致失败。
解决方案:使用 evm-connector
脚本生成 payload,成功调用 redeemUnderlying
,赎回 ETH。
经验教训:
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!