Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions evm/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,36 @@ task("deploy-token-impl", "Deploys the BridgeToken implementation").setAction(as
)
})

task("deploy-token-proxy", "Deploy BridgeToken behind a UUPS proxy")
.addParam("name", "Token name")
.addParam("symbol", "Token symbol")
.addOptionalParam("decimals", "Token decimals", "18")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to add scripts for Hyperliquid token with system address.
Also the system address should be retrieved from the rpc in the script

.setAction(async (args, hre) => {
const { ethers, upgrades } = hre

const name = String(args.name)
const symbol = String(args.symbol)
const decimals = Number(args.decimals)

const [deployer] = await ethers.getSigners()
console.log("Deployer:", deployer.address)
console.log("Params:", { name, symbol, decimals })

const BridgeToken = await ethers.getContractFactory("BridgeToken")

const proxy = await upgrades.deployProxy(BridgeToken, [name, symbol, decimals], {
initializer: "initialize",
})

await proxy.waitForDeployment()
const proxyAddress = await proxy.getAddress()

const implAddress = await upgrades.erc1967.getImplementationAddress(proxyAddress)

console.log("Proxy deployed to:", proxyAddress)
console.log("Implementation:", implAddress)
})

task("upgrade-bridge-token", "Upgrades a BridgeToken to a new implementation")
.addParam("factory", "The address of the OmniBridge contract")
.addParam("nearTokenAccount", "The NEAR token ID")
Expand Down Expand Up @@ -301,6 +331,13 @@ const config: HardhatUserConfig = {
url: `https://polygon-mainnet.infura.io/v3/${INFURA_API_KEY}`,
accounts: [`${EVM_PRIVATE_KEY}`],
},
hyperEvmMainnet: {
wormholeAddress: "0x7C0faFc4384551f063e05aee704ab943b8B53aB3",
omniChainId: 9,
chainId: 999,
url: "https://rpc.hyperliquid.xyz/evm",
accounts: [`${EVM_PRIVATE_KEY}`],
},
sepolia: {
omniChainId: 0,
chainId: 11155111,
Expand Down Expand Up @@ -335,6 +372,13 @@ const config: HardhatUserConfig = {
url: `https://polygon-amoy.infura.io/v3/${INFURA_API_KEY}`,
accounts: [`${EVM_PRIVATE_KEY}`],
},
hyperEvmTestnet: {
wormholeAddress: "0xBB73cB66C26740F31d1FabDC6b7A46a038A300dd",
omniChainId: 9,
chainId: 998,
url: "https://rpc.hyperliquid-testnet.xyz/evm",
accounts: [`${EVM_PRIVATE_KEY}`],
},
},
etherscan: {
apiKey: ETHERSCAN_API_KEY,
Expand Down
99 changes: 99 additions & 0 deletions evm/utils/hyperliquid/link_tokens.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
from typing import TypedDict, Literal, Union

import requests
from eth_account import Account
from eth_account.signers.local import LocalAccount
from web3 import Web3
from web3.middleware import SignAndSendRawMiddlewareBuilder
from hyperliquid.utils import constants
from hyperliquid.utils.signing import get_timestamp_ms, sign_l1_action

CreateInputParams = TypedDict("CreateInputParams", {"nonce": int})
CreateInput = TypedDict("CreateInput", {"create": CreateInputParams})
FinalizeEvmContractInput = Union[Literal["firstStorageSlot"], CreateInput]
FinalizeEvmContractAction = TypedDict(
"FinalizeEvmContractAction",
{"type": Literal["finalizeEvmContract"], "token": int, "input": FinalizeEvmContractInput},
)

DEFAULT_CONTRACT_ADDRESS = Web3.to_checksum_address(
"0x2E98e98aB34b42b14FeC9d431F7B051B232Ba133" # change this to your contract address if you are skipping deploying
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add make file for theses scripts and allow passing needed arguments over it

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, they have rust sdk, so it was possible to add these scripts to our cli
https://github.com/hyperliquid-dex/hyperliquid-rust-sdk

)
TOKEN = 1562 # note that if changing this you likely should also change the abi to have a different name and perhaps also different decimals and initial supply
PRIVATE_KEY = "0xPRIVATE_KEY" # Change this to your private key

# Connect to the JSON-RPC endpoint
rpc_url = "https://rpc.hyperliquid-testnet.xyz/evm"

contract_address = DEFAULT_CONTRACT_ADDRESS

def requestEvmContract(account):
assert contract_address is not None
action = {
"type": "spotDeploy",
"requestEvmContract": {
"token": TOKEN,
"address": contract_address.lower(),
"evmExtraWeiDecimals": 10,
},
}
nonce = get_timestamp_ms()
signature = sign_l1_action(account, action, None, nonce, None, False)
payload = {
"action": action,
"nonce": nonce,
"signature": signature,
"vaultAddress": None,
}
response = requests.post(constants.TESTNET_API_URL + "/exchange", json=payload)
print(response.json())

def finalizeEvmContract(account):
creation_nonce = 4
print(creation_nonce)
use_create_finalization = True
finalize_action: FinalizeEvmContractAction
if use_create_finalization:
finalize_action = {
"type": "finalizeEvmContract",
"token": TOKEN,
"input": {"create": {"nonce": creation_nonce}},
}
else:
finalize_action = {"type": "finalizeEvmContract", "token": TOKEN, "input": "firstStorageSlot"}
nonce = get_timestamp_ms()
signature = sign_l1_action(account, finalize_action, None, nonce, None, False)
payload = {
"action": finalize_action,
"nonce": nonce,
"signature": signature,
"vaultAddress": None,
}
response = requests.post(constants.TESTNET_API_URL + "/exchange", json=payload)
print(response.json())


def main():
w3 = Web3(Web3.HTTPProvider(rpc_url))

# The account will be used both for deploying the ERC20 contract and linking it to your native spot asset
# You can also switch this to create an account a different way if you don't want to include a secret key in code
if PRIVATE_KEY == "0xPRIVATE_KEY":
raise Exception("must set private key or create account another way")
account: LocalAccount = Account.from_key(PRIVATE_KEY)
print(f"Running with address {account.address}")
w3.middleware_onion.add(SignAndSendRawMiddlewareBuilder.build(account))
w3.eth.default_account = account.address
# Verify connection
if not w3.is_connected():
raise Exception("Failed to connect to the Ethereum network")

print(TOKEN, contract_address.lower())
#requestEvmContract(account)
finalizeEvmContract(account)

if __name__ == "__main__":
main()

# curl -s https://api.hyperliquid-testnet.xyz/info -H "Content-Type: application/json" -d '{"type": "tokenDetails", "tokenId": "0x646586ef3576346a4fcc9548909c1cba"}' | jq
# curl -s https://api.hyperliquid-testnet.xyz/info -H "Content-Type: application/json" -d '{ "type": "spotMeta" }' | jq '.tokens[] | select(.name=="JHWL")'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be cleaned up

123 changes: 123 additions & 0 deletions evm/utils/hyperliquid/spot_deploy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import getpass
import json
import os

import eth_account
from eth_account.signers.local import LocalAccount

from hyperliquid.exchange import Exchange
from hyperliquid.info import Info
from hyperliquid.utils import constants

def setup(base_url=None, skip_ws=False, perp_dexs=None):
config_path = os.path.join(os.path.dirname(__file__), "config.json")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the config file?

with open(config_path) as f:
config = json.load(f)
account: LocalAccount = eth_account.Account.from_key(get_secret_key(config))
address = config["account_address"]
if address == "":
address = account.address
print("Running with account address:", address)
if address != account.address:
print("Running with agent address:", account.address)
info = Info(base_url, skip_ws, perp_dexs=perp_dexs)
user_state = info.user_state(address)
spot_user_state = info.spot_user_state(address)
margin_summary = user_state["marginSummary"]
if float(margin_summary["accountValue"]) == 0 and len(spot_user_state["balances"]) == 0:
print("Not running the example because the provided account has no equity.")
url = info.base_url.split(".", 1)[1]
error_string = f"No accountValue:\nIf you think this is a mistake, make sure that {address} has a balance on {url}.\nIf address shown is your API wallet address, update the config to specify the address of your account, not the address of the API wallet."
raise Exception(error_string)
exchange = Exchange(account, base_url, account_address=address, perp_dexs=perp_dexs)
return address, info, exchange


def get_secret_key(config):
return config["secret_key"]
def step1(exchange):
# Step 1: Registering the Token
#
# Takes part in the spot deploy auction and if successful, registers token "TEST0"
# with sz_decimals 2 and wei_decimals 8.
# The max gas is 10,000 HYPE and represents the max amount to be paid for the spot deploy auction.
register_token_result = exchange.spot_deploy_register_token("TEST0", 2, 8, 1000000000000, "Test token example")
print(register_token_result)
# If registration is successful, a token index will be returned. This token index is required for
# later steps in the spot deploy process.
if register_token_result["status"] == "ok":
token = register_token_result["response"]["data"]
else:
return
return token

def step2(address, exchange, token):
# Step 2: User Genesis
#
# User genesis can be called multiple times to associate balances to specific users and/or
# tokens for genesis.
user_genesis_result = exchange.spot_deploy_user_genesis(
token,
[
(address, "100000000900000000"),
],
[],
)
print(user_genesis_result)

def step3(exchange, token):
# Step 3: Genesis
#
# Finalize genesis. The max supply of 300000000000000 wei needs to match the total
# allocation above from user genesis.
#
# "noHyperliquidity" can also be set to disable hyperliquidity. In that case, no balance
# should be associated with hyperliquidity from step 2 (user genesis).
genesis_result = exchange.spot_deploy_genesis(token, "100000000900000000", True)
print(genesis_result)

def step4(exchange, token):
# Step 4: Register Spot
#
# Register the spot pair (TEST0/USDC) given base and quote token indices. 0 represents USDC.
# The base token is the first token in the pair and the quote token is the second token.
register_spot_result = exchange.spot_deploy_register_spot(token, 0)
print(register_spot_result)
# If registration is successful, a spot index will be returned. This spot index is required for
# registering hyperliquidity.
if register_spot_result["status"] == "ok":
spot = register_spot_result["response"]["data"]
else:
return

return spot

def step5(exchange, spot):

# Step 5: Register Hyperliquidity
#
# Registers hyperliquidity for the spot pair. In this example, hyperliquidity is registered
# with a starting price of $2, an order size of 4, and 100 total orders.
#
# This step is required even if "noHyperliquidity" was set to True.
# If "noHyperliquidity" was set to True during step 3 (genesis), then "n_orders" is required to be 0.
register_hyperliquidity_result = exchange.spot_deploy_register_hyperliquidity(spot, 2.0, 4.0, 0, None)
print(register_hyperliquidity_result)

def main():
address, info, exchange = setup(constants.TESTNET_API_URL, skip_ws=True)
print(address, info, exchange)

# token = step1()
# token = 1562
# step2(address, exchange, token)
# step3(exchange, token)
# spot = step4(exchange, token)
# print(spot)
# spot = 1436
# step5(exchange, spot)

if __name__ == "__main__":
main()

# curl -s https://api.hyperliquid-testnet.xyz/info -H "Content-Type: application/json" -d '{"type":"spotDeployState","user":"0x36279BeA31b1CC48dd4454a2C7149f331eF3f3c3"}' | jq
Comment on lines +111 to +123
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be cleaned up

Loading