Skip to content

Commit

Permalink
Merge branch 'main' into mat/reduce-gas-oracle-txs
Browse files Browse the repository at this point in the history
  • Loading branch information
matthiasmatt authored Jan 12, 2025
2 parents 919640e + 8db476f commit be6fd28
Show file tree
Hide file tree
Showing 38 changed files with 2,511 additions and 385 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#2144](https://github.com/NibiruChain/nibiru/pull/2144) - feat(token-registry): Implement strongly typed Nibiru Token Registry and generation command
- [#2145](https://github.com/NibiruChain/nibiru/pull/2145) - chore(token-registry): add xNIBI Astrovault LST to registry
- [#2147](https://github.com/NibiruChain/nibiru/pull/2147) - fix(simapp): manually add x/vesting Cosmos-SDK module types to the codec in simulation tests since they are expected by default
- [#2149](https://github.com/NibiruChain/nibiru/pull/2149) - feat(evm-oracle):
add Solidity contract that we can use to expose the Nibiru Oracle in the
ChainLink interface. Publish all precompiled contracts and ABIs on npm under
the `@nibiruchain/solidity` package.
- [#2151](https://github.com/NibiruChain/nibiru/pull/2151) - feat(evm): randao support for evm
- [#2152](https://github.com/NibiruChain/nibiru/pull/2152) - fix(precompile): consume gas for precompile calls regardless of error
- [#2154](https://github.com/NibiruChain/nibiru/pull/2154) - fix(evm):
JSON encoding for the `EIP55Addr` struct was not following the Go conventions and
needed to include double quotes around the hexadecimal string.
- [#2156](https://github.com/NibiruChain/nibiru/pull/2156) - test(evm-e2e): add E2E test using the Nibiru Oracle's ChainLink impl
- [#2157](https://github.com/NibiruChain/nibiru/pull/2157) - fix(evm): Fix unit inconsistency related to AuthInfo.Fee and txData.Fee using effective fee
- [#2160](https://github.com/NibiruChain/nibiru/pull/2160) - fix(evm-precompile): use bank.MsgServer Send in precompile IFunToken.bankMsgSend
- [#2162](https://github.com/NibiruChain/nibiru/pull/2162) - test(testutil): try retrying for 'panic: pebbledb: closed'

#### Nibiru EVM | Before Audit 2 - 2024-12-06

Expand Down
4 changes: 3 additions & 1 deletion app/evmante/evmante_validate_basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,12 @@ func (vbd EthValidateBasicDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simu
)
}

// Compute fees using effective fee to enforce 1unibi minimum gas price
effectiveFeeMicronibi := evm.WeiToNative(txData.EffectiveFeeWei(evm.BASE_FEE_WEI))
txFee = txFee.Add(
sdk.Coin{
Denom: evm.EVMBankDenom,
Amount: sdkmath.NewIntFromBigInt(txData.Fee()),
Amount: sdkmath.NewIntFromBigInt(effectiveFeeMicronibi),
},
)
}
Expand Down
4 changes: 3 additions & 1 deletion app/wasmext/wasm_cli_test/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,5 +140,7 @@ func (s *TestSuite) requiredDeployedContractsLen(total int) {
}

func TestIntegrationTestSuite(t *testing.T) {
suite.Run(t, new(TestSuite))
testutil.RetrySuiteRunIfDbClosed(t, func() {
suite.Run(t, new(TestSuite))
}, 2)
}
11 changes: 9 additions & 2 deletions eth/eip55.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package eth

import (
"encoding/json"
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -42,7 +43,7 @@ func (h EIP55Addr) Marshal() ([]byte, error) {
// Implements the gogo proto custom type interface.
// Ref: https://github.com/cosmos/gogoproto/blob/v1.5.0/custom_types.md
func (h EIP55Addr) MarshalJSON() ([]byte, error) {
return []byte(h.String()), nil
return json.Marshal(h.String())
}

// MarshalTo serializes a EIP55Addr directly into a pre-allocated byte slice ("data").
Expand All @@ -68,7 +69,13 @@ func (h *EIP55Addr) Unmarshal(data []byte) error {
// UnmarshalJSON implements the gogo proto custom type interface.
// Ref: https://github.com/cosmos/gogoproto/blob/v1.5.0/custom_types.md
func (h *EIP55Addr) UnmarshalJSON(bz []byte) error {
addr, err := NewEIP55AddrFromStr(string(bz))
var addrStr string
if err := json.Unmarshal(bz, &addrStr); err != nil {
return fmt.Errorf(
"EIP55AddrError: UnmarhsalJSON had invalid input %s: %w", bz, err,
)
}
addr, err := NewEIP55AddrFromStr(addrStr)
if err != nil {
return err
}
Expand Down
14 changes: 8 additions & 6 deletions eth/eip55_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package eth_test

import (
"fmt"
"strconv"
"strings"
"testing"

gethcommon "github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -116,15 +118,15 @@ func (s *EIP55AddrSuite) TestProtobufEncoding() {
}{
{
input: threeValidAddrs[0],
expectedJson: "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
expectedJson: `"0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"`,
},
{
input: threeValidAddrs[1],
expectedJson: "0xAe967917c465db8578ca9024c205720b1a3651A9",
expectedJson: `"0xAe967917c465db8578ca9024c205720b1a3651A9"`,
},
{
input: threeValidAddrs[2],
expectedJson: "0x1111111111111111111112222222222223333323",
expectedJson: `"0x1111111111111111111112222222222223333323"`,
},
} {
s.Run(strconv.Itoa(tcIdx), func() {
Expand All @@ -140,7 +142,7 @@ func (s *EIP55AddrSuite) TestProtobufEncoding() {

bz, err := tc.input.Marshal()
s.NoError(err)
s.Equal(tc.expectedJson, string(bz),
s.Equal(strings.Trim(tc.expectedJson, `"`), string(bz),
"Marshaling to bytes gives different value than the test case specifies. test case #%d", tcIdx)

err = eip55Addr.Unmarshal(bz)
Expand Down Expand Up @@ -188,10 +190,10 @@ func (s *EIP55AddrSuite) TestStringEncoding() {

bz, err := addr.MarshalJSON()
s.NoError(err)
s.Equal(addrHex, string(bz))
s.Equal(fmt.Sprintf(`"%s"`, addrHex), string(bz))

addrb := new(eth.EIP55Addr)
err = addrb.UnmarshalJSON([]byte(addrHex))
err = addrb.UnmarshalJSON([]byte(fmt.Sprintf(`"%s"`, addrHex)))
s.NoError(err)
s.EqualValues(addrb, addr)
}
5 changes: 4 additions & 1 deletion eth/rpc/rpcapi/eth_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ type NodeSuite struct {

func TestSuite_RunAll(t *testing.T) {
suite.Run(t, new(Suite))
suite.Run(t, new(NodeSuite))

testutil.RetrySuiteRunIfDbClosed(t, func() {
suite.Run(t, new(NodeSuite))
}, 2)
}

// SetupSuite runs before every test in the suite. Implements the
Expand Down
98 changes: 98 additions & 0 deletions evm-e2e/contracts/NibiruOracleChainLinkLike.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import '@nibiruchain/solidity/contracts/IOracle.sol';

/// @title NibiruOracleChainLinkLike
/// @notice This contract serves as a ChainLink-like data feed that sources its
/// "answer" value from the Nibiru Oracle system. The Nibiru Oracle gives price
/// data with 18 decimals universally, and that 18-decimal answer is scaled to
/// have the number of decimals specified by "decimals()". This is set at the
/// time of deployment.
/// _ _ _____ ____ _____ _____ _ _
/// | \ | ||_ _|| _ \|_ _|| __ \ | | | |
/// | \| | | | | |_) | | | | |__) || | | |
/// | . ` | | | | _ < | | | _ / | | | |
/// | |\ | _| |_ | |_) |_| |_ | | \ \ | |__| |
/// |_| \_||_____||____/|_____||_| \_\ \____/
///
contract NibiruOracleChainLinkLike is ChainLinkAggregatorV3Interface {
string public pair;
uint8 public _decimals;

constructor(string memory _pair, uint8 _dec) {
require(_dec <= 18, 'Decimals cannot exceed 18');
require(bytes(_pair).length > 0, 'Pair string cannot be empty');
pair = _pair;
_decimals = _dec;
}

function decimals() external view override returns (uint8) {
return _decimals;
}

/// @notice Returns a human-readable description of the oracle and its data
/// feed identifier (pair) in the Nibiru Oracle system
function description() external view override returns (string memory) {
return string.concat('Nibiru Oracle ChainLink-like price feed for ', pair);
}

/// @notice Oracle version number. Hardcoded to 1.
function version() external pure override returns (uint256) {
return 1;
}

/// @notice Returns the latest data from the Nibiru Oracle.
/// @return roundId The block number when the answer was published onchain.
/// @return answer Data feed result scaled to the precision specified by
/// "decimals()"
/// @return startedAt UNIX timestamp in seconds when "answer" was published.
/// @return updatedAt UNIX timestamp in seconds when "answer" was published.
/// @return answeredInRound The ID of the round where the answer was computed.
/// Since the Nibiru Oracle does not have ChainLink's system of voting
/// rounds, this argument is a meaningless, arbitrary constant.
function latestRoundData()
public
view
override
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
{
(
uint80 _roundId,
int256 answer18Dec,
uint256 _startedAt,
uint256 _updatedAt,
uint80 _answeredInRound
) = NIBIRU_ORACLE.chainLinkLatestRoundData(pair);
answer = scaleAnswerToDecimals(answer18Dec);
return (_roundId, answer, _startedAt, _updatedAt, _answeredInRound);
}

/// @notice Returns the latest data from the Nibiru Oracle. Historical round
/// retrieval is not supported. This method is a duplicate of
/// "latestRoundData".
/// @return roundId The block number when the answer was published onchain.
/// @return answer Data feed result scaled to the precision specified by
/// "decimals()"
/// @return startedAt UNIX timestamp in seconds when "answer" was published.
/// @return updatedAt UNIX timestamp in seconds when "answer" was published.
/// @return answeredInRound The ID of the round where the answer was computed.
/// Since the Nibiru Oracle does not have ChainLink's system of voting
/// rounds, this argument is a meaningless, arbitrary constant.
function getRoundData(
uint80
)
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
{
return latestRoundData();
}

function scaleAnswerToDecimals(int256 answer18Dec) internal view returns (int256 answer) {
// Default answers are in 18 decimals.
// Scale down to the decimals specified in the constructor.
uint8 pow10 = 18 - _decimals;
return answer18Dec / int256(10 ** pow10);
}
}
41 changes: 31 additions & 10 deletions evm-e2e/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions evm-e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"typechain": "^8.3.2",
"@nomicfoundation/hardhat-toolbox": "^5.0.0",
"@openzeppelin/contracts": "^5.1.0",
"@nibiruchain/solidity": "^0.0.2",
"hardhat": "^2.22.15"
},
"scripts": {
Expand Down
40 changes: 40 additions & 0 deletions evm-e2e/test/nibiru_oracle.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { expect, test } from '@jest/globals';
import { toBigInt } from 'ethers';
import { deployContractNibiruOracleChainLinkLike } from './utils';

test('NibiruOracleChainLinkLike implements ChainLink AggregatorV3Interface', async () => {
const { oraclePair, contract } = await deployContractNibiruOracleChainLinkLike();

const oracleAddr = await contract.getAddress();
expect(oracleAddr).not.toBeFalsy();

const decimals = await contract.decimals();
expect(decimals).toEqual(BigInt(8));

const description = await contract.description();
expect(description).toEqual(`Nibiru Oracle ChainLink-like price feed for ${oraclePair}`);

const version = await contract.version();
expect(version).toEqual(1n);

// latestRoundData
const genesisEthUsdPrice = 2000n;
{
const { roundId, answer, startedAt, updatedAt, answeredInRound } = await contract.latestRoundData();
expect(roundId).toEqual(0n); // price is from genesis block
expect(startedAt).toBeGreaterThan(1n);
expect(updatedAt).toBeGreaterThan(1n);
expect(answeredInRound).toEqual(420n);
expect(answer).toEqual(genesisEthUsdPrice * toBigInt(1e8));
}

// getRoundData
{
const { roundId, answer, startedAt, updatedAt, answeredInRound } = await contract.getRoundData(0n);
expect(roundId).toEqual(0n); // price is from genesis block
expect(startedAt).toBeGreaterThan(1n);
expect(updatedAt).toBeGreaterThan(1n);
expect(answeredInRound).toEqual(420n);
expect(answer).toEqual(genesisEthUsdPrice * toBigInt(1e8));
}
});
17 changes: 16 additions & 1 deletion evm-e2e/test/utils.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { account } from './setup';
import { parseEther, toBigInt, TransactionRequest, Wallet } from 'ethers';
import { ContractTransactionResponse, parseEther, toBigInt, TransactionRequest, Wallet } from 'ethers';
import {
InifiniteLoopGas__factory,
SendNibi__factory,
TestERC20__factory,
EventsEmitter__factory,
TransactionReverter__factory,
NibiruOracleChainLinkLike__factory,
NibiruOracleChainLinkLike,
} from '../types';

export const alice = Wallet.createRandom();
Expand Down Expand Up @@ -66,3 +68,16 @@ export const sendTestNibi = async () => {
console.log(txResponse);
return txResponse;
};

export const deployContractNibiruOracleChainLinkLike = async (): Promise<{
oraclePair: string;
contract: NibiruOracleChainLinkLike & {
deploymentTransaction(): ContractTransactionResponse;
};
}> => {
const oraclePair = 'ueth:uuusd';
const factory = new NibiruOracleChainLinkLike__factory(account);
const contract = await factory.deploy(oraclePair, toBigInt(8));
await contract.waitForDeployment();
return { oraclePair, contract };
};
Loading

0 comments on commit be6fd28

Please sign in to comment.