-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #61 from orochi-network/feature/redeem_beta_token_…
…with_signature Feature: Redeem beta token with signature
- Loading branch information
Showing
6 changed files
with
227 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.8.0; | ||
import '@openzeppelin/contracts/utils/cryptography/ECDSA.sol'; | ||
import '../libraries/Bytes.sol'; | ||
|
||
error InvalidSignature(address signer); | ||
error InvalidNonce(address receiverAddress, uint64 nonce); | ||
error InvalidChain(uint256 chainId); | ||
|
||
contract XOROECDSA { | ||
// Using Bytes for bytes | ||
using Bytes for bytes; | ||
|
||
// Verifiy digital signature | ||
using ECDSA for bytes; | ||
using ECDSA for bytes32; | ||
|
||
// Storage of recent epoch's result | ||
mapping(address => uint256) private nonceStorage; | ||
|
||
// Structure of ECDSA proof of XORO | ||
struct XOROECDSAProof { | ||
address signer; | ||
uint96 chainId; | ||
address beneficiary; | ||
uint64 nonce; | ||
uint192 value; | ||
} | ||
|
||
// Verify proof of operator | ||
// uint96 chainId; | ||
// address beneficiary; | ||
// uint64 nonce; | ||
// uint192 value; | ||
function _decodeProof(bytes memory proof) internal pure returns (XOROECDSAProof memory ecdsaProof) { | ||
bytes memory signature = proof.readBytes(0, 65); | ||
bytes memory message = proof.readBytes(65, 64); | ||
uint256 uin256Value = message.readUint256(0); | ||
ecdsaProof.chainId = uint96(uin256Value >> 160); | ||
ecdsaProof.beneficiary = address(uint160(uin256Value)); | ||
uin256Value = message.readUint256(32); | ||
ecdsaProof.nonce = uint64(uin256Value >> 192); | ||
ecdsaProof.value = uint192(uin256Value); | ||
ecdsaProof.signer = message.toEthSignedMessageHash().recover(signature); | ||
return ecdsaProof; | ||
} | ||
|
||
// Verify nonce | ||
function _getNonce(address singerAddress) internal view returns (uint256) { | ||
return nonceStorage[singerAddress]; | ||
} | ||
|
||
// Verify nonce | ||
function _verifyNonce(address receiverAddress, uint64 nonce) internal view returns (bool) { | ||
return nonceStorage[receiverAddress] == nonce; | ||
} | ||
|
||
// Increase nonce | ||
function _increaseNonce(address receiverAddress) internal { | ||
nonceStorage[receiverAddress] += 1; | ||
} | ||
|
||
// Get nonce of a given address | ||
function getNonce(address receiverAddress) external view returns (uint256) { | ||
return _getNonce(receiverAddress); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,75 @@ | ||
import hre, { upgrades } from 'hardhat'; | ||
import { OrocleV2 } from '../typechain-types'; | ||
import { FixedFloat, OrocleEncoding } from '@orochi-network/utilities'; | ||
import { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers'; | ||
|
||
describe('OrocleV1', function () { | ||
it('OrocleV1 should be upgradeable', async () => { | ||
let orocleV2: OrocleV2; | ||
let operator: HardhatEthersSigner; | ||
|
||
describe('OrocleV2', function () { | ||
it('OrocleV2 should be upgradeable', async () => { | ||
const { ethers } = hre; | ||
const accounts = await ethers.getSigners(); | ||
const orocleV1Factory = await ethers.getContractFactory('OrocleTest'); | ||
const orocleV2Factory = await ethers.getContractFactory('OrocleV1'); | ||
[operator] = await ethers.getSigners(); | ||
const orocleV2Factory1 = await ethers.getContractFactory('OrocleTest'); | ||
const orocleV2Factory2 = await ethers.getContractFactory('OrocleV2'); | ||
|
||
const instance = await upgrades.deployProxy(orocleV1Factory, [accounts[0]]); | ||
const instance = await upgrades.deployProxy(orocleV2Factory1, [[operator.address]]); | ||
|
||
console.log('Instance:', await instance.owner(), await instance.getAddress()); | ||
|
||
const upgraded = await upgrades.upgradeProxy(await instance.getAddress(), orocleV2Factory); | ||
const upgraded = await upgrades.upgradeProxy(await instance.getAddress(), orocleV2Factory2); | ||
|
||
console.log('Upgrade', await upgraded.owner(), await upgraded.getAddress()); | ||
orocleV2 = upgraded as any; | ||
}); | ||
|
||
it('OrocleV2 should be upgradeable', async () => { | ||
await orocleV2.connect(operator).publishPrice( | ||
OrocleEncoding.encodeTokenPrice([ | ||
{ | ||
symbol: 'BTC', | ||
price: 42000n * 10n ** 18n, | ||
}, | ||
]), | ||
); | ||
const [round, lastUpdate, price] = await orocleV2.getLatestRound(1, OrocleEncoding.toIdentifier('BTC')); | ||
|
||
console.log('Round data:', { round, lastUpdate, price }); | ||
|
||
console.log( | ||
'BTC/USDT', | ||
FixedFloat.fromFixedFloat({ | ||
basedValue: BigInt(price), | ||
decimals: 18, | ||
}).pretty('en-us', 2), | ||
); | ||
}); | ||
|
||
it('OrocleV2 should be upgradeable', async () => { | ||
await orocleV2.connect(operator).publishPrice( | ||
OrocleEncoding.encodeTokenPrice([ | ||
{ | ||
symbol: 'BTC', | ||
price: 42000n * 10n ** 18n, | ||
}, | ||
{ | ||
symbol: 'ETH', | ||
price: 3821n * 10n ** 18n, | ||
}, | ||
]), | ||
); | ||
const [round, lastUpdate, price] = await orocleV2.getLatestRound(1, OrocleEncoding.toIdentifier('ETH')); | ||
|
||
console.log('Round data:', { round, lastUpdate, price }); | ||
|
||
console.log( | ||
'ETH/USDT', | ||
FixedFloat.fromFixedFloat({ | ||
basedValue: BigInt(price), | ||
decimals: 18, | ||
}).pretty('en-us', 2), | ||
); | ||
|
||
console.log(await orocleV2.getLatestRound(1, OrocleEncoding.toIdentifier('BTC'))); | ||
}); | ||
}); |
Oops, something went wrong.