Added commands to wrap and unwrap SOL.

This commit is contained in:
Geoff Taylor 2021-06-02 20:03:46 +01:00
parent e0c552ca55
commit cd7cdab540
6 changed files with 487 additions and 67 deletions

View File

@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "markdown",
"id": "biological-pontiac",
"id": "interesting-continuity",
"metadata": {},
"source": [
"# ⚠ Warning\n",
@ -16,7 +16,7 @@
},
{
"cell_type": "markdown",
"id": "moral-enemy",
"id": "chemical-paper",
"metadata": {},
"source": [
"# 🥭 BaseModel\n",
@ -33,7 +33,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "reverse-trade",
"id": "herbal-bracket",
"metadata": {
"jupyter": {
"source_hidden": true
@ -69,7 +69,7 @@
},
{
"cell_type": "markdown",
"id": "forty-booth",
"id": "aboriginal-active",
"metadata": {},
"source": [
"## Version enum\n",
@ -80,7 +80,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "honest-development",
"id": "stretch-analyst",
"metadata": {},
"outputs": [],
"source": [
@ -95,7 +95,7 @@
},
{
"cell_type": "markdown",
"id": "western-cookbook",
"id": "cooked-india",
"metadata": {},
"source": [
"## InstructionType enum\n",
@ -106,7 +106,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "reverse-patch",
"id": "composite-scotland",
"metadata": {},
"outputs": [],
"source": [
@ -135,7 +135,7 @@
},
{
"cell_type": "markdown",
"id": "presidential-glass",
"id": "rubber-wisconsin",
"metadata": {},
"source": [
"## Internal functions\n",
@ -146,7 +146,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "recreational-success",
"id": "certain-particular",
"metadata": {},
"outputs": [],
"source": [
@ -162,7 +162,7 @@
},
{
"cell_type": "markdown",
"id": "pacific-medline",
"id": "innovative-conditioning",
"metadata": {},
"source": [
"## AccountInfo class\n"
@ -171,7 +171,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "framed-miller",
"id": "beneficial-weekly",
"metadata": {},
"outputs": [],
"source": [
@ -243,7 +243,7 @@
},
{
"cell_type": "markdown",
"id": "developing-immune",
"id": "planned-referral",
"metadata": {},
"source": [
"## AddressableAccount class\n",
@ -256,7 +256,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "ruled-concern",
"id": "expired-stopping",
"metadata": {},
"outputs": [],
"source": [
@ -275,7 +275,7 @@
},
{
"cell_type": "markdown",
"id": "convinced-princeton",
"id": "unsigned-circus",
"metadata": {},
"source": [
"## SerumAccountFlags class\n",
@ -286,7 +286,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "handmade-browser",
"id": "spare-large",
"metadata": {},
"outputs": [],
"source": [
@ -329,7 +329,7 @@
},
{
"cell_type": "markdown",
"id": "dramatic-armor",
"id": "substantial-worcester",
"metadata": {},
"source": [
"## MangoAccountFlags class\n",
@ -340,7 +340,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "thorough-helicopter",
"id": "integral-uniform",
"metadata": {},
"outputs": [],
"source": [
@ -373,7 +373,7 @@
},
{
"cell_type": "markdown",
"id": "empty-sheffield",
"id": "tired-collins",
"metadata": {},
"source": [
"## Index class"
@ -382,7 +382,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "partial-decimal",
"id": "designed-syndicate",
"metadata": {},
"outputs": [],
"source": [
@ -409,7 +409,7 @@
},
{
"cell_type": "markdown",
"id": "optimum-milton",
"id": "democratic-thread",
"metadata": {},
"source": [
"## AggregatorConfig class"
@ -418,7 +418,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "parallel-absence",
"id": "coordinate-headquarters",
"metadata": {},
"outputs": [],
"source": [
@ -451,7 +451,7 @@
},
{
"cell_type": "markdown",
"id": "increased-leone",
"id": "therapeutic-audit",
"metadata": {},
"source": [
"## Round class"
@ -460,7 +460,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "compatible-toronto",
"id": "liquid-solution",
"metadata": {},
"outputs": [],
"source": [
@ -485,7 +485,7 @@
},
{
"cell_type": "markdown",
"id": "improved-promise",
"id": "rural-candidate",
"metadata": {},
"source": [
"## Answer class"
@ -494,7 +494,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "dying-insurance",
"id": "graphic-release",
"metadata": {},
"outputs": [],
"source": [
@ -520,7 +520,7 @@
},
{
"cell_type": "markdown",
"id": "straight-purse",
"id": "established-exemption",
"metadata": {},
"source": [
"## Aggregator class"
@ -529,7 +529,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "conceptual-harvest",
"id": "refined-alexander",
"metadata": {},
"outputs": [],
"source": [
@ -595,7 +595,7 @@
},
{
"cell_type": "markdown",
"id": "medical-rogers",
"id": "varied-batch",
"metadata": {},
"source": [
"## Token class\n",
@ -606,7 +606,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "solar-chambers",
"id": "three-legislature",
"metadata": {},
"outputs": [],
"source": [
@ -661,7 +661,7 @@
},
{
"cell_type": "markdown",
"id": "higher-carbon",
"id": "optimum-packaging",
"metadata": {},
"source": [
"## SolToken object\n",
@ -672,7 +672,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "motivated-suite",
"id": "resistant-lawsuit",
"metadata": {},
"outputs": [],
"source": [
@ -681,7 +681,7 @@
},
{
"cell_type": "markdown",
"id": "prime-accused",
"id": "dimensional-interference",
"metadata": {},
"source": [
"## TokenLookup class\n",
@ -703,7 +703,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "careful-thanksgiving",
"id": "attached-stocks",
"metadata": {},
"outputs": [],
"source": [
@ -743,7 +743,7 @@
},
{
"cell_type": "markdown",
"id": "intelligent-harmony",
"id": "informative-fortune",
"metadata": {},
"source": [
"## SpotMarket class"
@ -752,7 +752,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "attempted-prayer",
"id": "rational-drain",
"metadata": {},
"outputs": [],
"source": [
@ -776,7 +776,7 @@
},
{
"cell_type": "markdown",
"id": "abandoned-network",
"id": "cordless-diversity",
"metadata": {},
"source": [
"## SpotMarketLookup class\n",
@ -801,7 +801,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "grateful-child",
"id": "civic-sunset",
"metadata": {},
"outputs": [],
"source": [
@ -883,7 +883,7 @@
},
{
"cell_type": "markdown",
"id": "affecting-orlando",
"id": "satellite-shadow",
"metadata": {},
"source": [
"## BasketToken class\n",
@ -894,7 +894,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "consistent-senator",
"id": "breathing-drove",
"metadata": {},
"outputs": [],
"source": [
@ -950,7 +950,7 @@
},
{
"cell_type": "markdown",
"id": "swiss-opinion",
"id": "centered-locking",
"metadata": {},
"source": [
"## TokenValue class\n",
@ -961,7 +961,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "strange-pierre",
"id": "limited-wrapping",
"metadata": {},
"outputs": [],
"source": [
@ -1046,7 +1046,7 @@
},
{
"cell_type": "markdown",
"id": "forty-quebec",
"id": "metallic-director",
"metadata": {},
"source": [
"## OwnedTokenValue class\n",
@ -1057,7 +1057,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "lovely-association",
"id": "honey-longitude",
"metadata": {},
"outputs": [],
"source": [
@ -1097,7 +1097,7 @@
},
{
"cell_type": "markdown",
"id": "ceramic-walker",
"id": "capable-sauce",
"metadata": {},
"source": [
"## MarketMetadata class"
@ -1106,7 +1106,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "exact-writer",
"id": "continent-bookmark",
"metadata": {},
"outputs": [],
"source": [
@ -1144,7 +1144,7 @@
},
{
"cell_type": "markdown",
"id": "intellectual-biotechnology",
"id": "elect-principal",
"metadata": {},
"source": [
"## Group class\n",
@ -1155,7 +1155,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "adverse-crisis",
"id": "premier-vaccine",
"metadata": {},
"outputs": [],
"source": [
@ -1414,7 +1414,7 @@
},
{
"cell_type": "markdown",
"id": "opposed-front",
"id": "rotary-therapist",
"metadata": {},
"source": [
"## TokenAccount class"
@ -1423,7 +1423,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "clean-laundry",
"id": "little-portfolio",
"metadata": {},
"outputs": [],
"source": [
@ -1502,12 +1502,12 @@
" return TokenAccount.parse(account_info)\n",
"\n",
" def __str__(self) -> str:\n",
" return f\"« Token: Mint: {self.mint}, Owner: {self.owner}, Amount: {self.amount} »\"\n"
" return f\"« Token: Address: {self.address}, Mint: {self.mint}, Owner: {self.owner}, Amount: {self.amount} »\"\n"
]
},
{
"cell_type": "markdown",
"id": "blocked-sudan",
"id": "exact-emergency",
"metadata": {},
"source": [
"## OpenOrders class\n"
@ -1516,7 +1516,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "taken-shock",
"id": "spanish-promise",
"metadata": {},
"outputs": [],
"source": [
@ -1636,7 +1636,7 @@
},
{
"cell_type": "markdown",
"id": "elder-retailer",
"id": "listed-error",
"metadata": {},
"source": [
"## BalanceSheet class"
@ -1645,7 +1645,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "featured-bandwidth",
"id": "vulnerable-conjunction",
"metadata": {},
"outputs": [],
"source": [
@ -1692,7 +1692,7 @@
},
{
"cell_type": "markdown",
"id": "manual-bubble",
"id": "seeing-victorian",
"metadata": {},
"source": [
"## MarginAccount class\n"
@ -1701,7 +1701,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "expected-winner",
"id": "sunset-chase",
"metadata": {},
"outputs": [],
"source": [
@ -1952,7 +1952,7 @@
},
{
"cell_type": "markdown",
"id": "pointed-coral",
"id": "extraordinary-mozambique",
"metadata": {},
"source": [
"## MarginAccountMetadata class"
@ -1961,7 +1961,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "approved-giving",
"id": "connected-cattle",
"metadata": {},
"outputs": [],
"source": [
@ -1987,7 +1987,7 @@
},
{
"cell_type": "markdown",
"id": "fossil-reserve",
"id": "green-boxing",
"metadata": {},
"source": [
"# Events"
@ -1995,7 +1995,7 @@
},
{
"cell_type": "markdown",
"id": "blessed-chain",
"id": "biological-penny",
"metadata": {},
"source": [
"## LiquidationEvent"
@ -2004,7 +2004,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "parallel-ethics",
"id": "affecting-hydrogen",
"metadata": {},
"outputs": [],
"source": [
@ -2040,7 +2040,7 @@
},
{
"cell_type": "markdown",
"id": "threatened-union",
"id": "geological-pantyhose",
"metadata": {},
"source": [
"# ✅ Testing"
@ -2049,7 +2049,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "internal-thong",
"id": "saving-lending",
"metadata": {},
"outputs": [],
"source": [
@ -2128,7 +2128,7 @@
},
{
"cell_type": "markdown",
"id": "virgin-waterproof",
"id": "insured-tackle",
"metadata": {},
"source": [
"# 🏃 Running"
@ -2137,7 +2137,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "color-ultimate",
"id": "identified-margin",
"metadata": {},
"outputs": [],
"source": [

88
bin/close-wrapped-sol-account Executable file
View File

@ -0,0 +1,88 @@
#!/usr/bin/env pyston3
import os
import sys
from pathlib import Path
# Get the full path to this script.
script_path = Path(os.path.realpath(__file__))
# The parent of the script is the bin directory.
# The parent of the bin directory is the notebook directory.
# It's this notebook directory we want.
notebook_directory = script_path.parent.parent
# Add the notebook directory to our import path.
sys.path.append(str(notebook_directory))
# Add the startup directory to our import path.
startup_directory = notebook_directory / "meta" / "startup"
sys.path.append(str(startup_directory))
import argparse
import logging
import projectsetup # noqa: F401
import typing
from solana.account import Account
from solana.publickey import PublicKey
from spl.token.constants import TOKEN_PROGRAM_ID
from solana.transaction import Transaction
from spl.token.instructions import CloseAccountParams, close_account
from BaseModel import TokenAccount, TokenLookup
from Constants import WARNING_DISCLAIMER_TEXT
from Context import default_context as context
from Wallet import Wallet
parser = argparse.ArgumentParser(description='Creates a new wallet and private key, and saves it to a file.')
parser.add_argument("--id-file", type=str, default="id.json",
help="file containing the JSON-formatted wallet private key")
parser.add_argument("--address", type=PublicKey,
help="Public key of the Wrapped SOL account to close")
parser.add_argument("--log-level", default=logging.WARNING, type=lambda level: getattr(logging, level),
help="level of verbosity to log (possible values: DEBUG, INFO, WARNING, ERROR, CRITICAL)")
parser.add_argument("--overwrite", action="store_true", default=False,
help="overwrite the ID file, if it exists (use with care!)")
args = parser.parse_args()
logging.getLogger().setLevel(args.log_level)
logging.warning(WARNING_DISCLAIMER_TEXT)
id_filename = args.id_file
if not os.path.isfile(id_filename):
logging.error(f"Wallet file '{id_filename}' is not present.")
sys.exit(1)
wallet = Wallet.load(id_filename)
lookups = TokenLookup.default_lookups()
wrapped_sol = lookups.find_by_symbol("SOL")
token_account = TokenAccount.load(context, args.address)
if (token_account is None) or (token_account.mint != wrapped_sol.mint):
raise Exception(f"Account {args.address} is not a {wrapped_sol.name} account.")
transaction = Transaction()
signers: typing.List[Account] = [wallet.account]
payer = wallet.address
transaction.add(
close_account(
CloseAccountParams(
account=args.address,
owner=wallet.address,
dest=wallet.address,
program_id=TOKEN_PROGRAM_ID,
)
)
)
print(f"Closing account: {args.address} with balance {token_account.amount} lamports.")
response = context.client.send_transaction(transaction, *signers)
transaction_id = context.unwrap_transaction_id_or_raise_exception(response)
print(f"Waiting on transaction ID: {transaction_id}")
context.wait_for_confirmation(transaction_id)
print("Done.")

View File

@ -31,7 +31,7 @@ from solana.publickey import PublicKey
from solana.system_program import TransferParams, transfer
from solana.transaction import Transaction
from Constants import WARNING_DISCLAIMER_TEXT
from Constants import SOL_DECIMAL_DIVISOR, WARNING_DISCLAIMER_TEXT
from Context import Context, default_cluster, default_cluster_url, default_program_id, default_dex_program_id, default_group_name, default_group_id
from Wallet import Wallet
@ -81,7 +81,7 @@ try:
print(f"Balance: {sol_balance} SOL")
# "A lamport has a value of 0.000000001 SOL." from https://docs.solana.com/introduction
lamports = int(args.quantity * Decimal(10 ** 9))
lamports = int(args.quantity * SOL_DECIMAL_DIVISOR)
source = wallet.address
destination = args.address

62
bin/show-wrapped-sol Executable file
View File

@ -0,0 +1,62 @@
#!/usr/bin/env pyston3
import os
import sys
from pathlib import Path
# Get the full path to this script.
script_path = Path(os.path.realpath(__file__))
# The parent of the script is the bin directory.
# The parent of the bin directory is the notebook directory.
# It's this notebook directory we want.
notebook_directory = script_path.parent.parent
# Add the notebook directory to our import path.
sys.path.append(str(notebook_directory))
# Add the startup directory to our import path.
startup_directory = notebook_directory / "meta" / "startup"
sys.path.append(str(startup_directory))
import argparse
import logging
import projectsetup # noqa: F401
from BaseModel import TokenAccount, TokenLookup
from Constants import WARNING_DISCLAIMER_TEXT
from Context import default_context as context
from Wallet import Wallet
parser = argparse.ArgumentParser(description='Creates a new wallet and private key, and saves it to a file.')
parser.add_argument("--id-file", type=str, default="id.json",
help="file containing the JSON-formatted wallet private key")
parser.add_argument("--log-level", default=logging.WARNING, type=lambda level: getattr(logging, level),
help="level of verbosity to log (possible values: DEBUG, INFO, WARNING, ERROR, CRITICAL)")
parser.add_argument("--overwrite", action="store_true", default=False,
help="overwrite the ID file, if it exists (use with care!)")
args = parser.parse_args()
logging.getLogger().setLevel(args.log_level)
logging.warning(WARNING_DISCLAIMER_TEXT)
id_filename = args.id_file
if not os.path.isfile(id_filename):
logging.error(f"Wallet file '{id_filename}' is not present.")
sys.exit(1)
wallet = Wallet.load(id_filename)
lookups = TokenLookup.default_lookups()
wrapped_sol = lookups.find_by_symbol("SOL")
token_accounts = TokenAccount.fetch_all_for_owner_and_token(context, wallet.address, wrapped_sol)
if len(token_accounts) == 0:
print("No wrapped SOL accounts")
else:
print(f"{wrapped_sol.name}:")
for account in token_accounts:
print(f" {account.address}: {account.amount:>18,.8f} {wrapped_sol.symbol}")

132
bin/unwrap-sol Executable file
View File

@ -0,0 +1,132 @@
#!/usr/bin/env pyston3
import os
import sys
from pathlib import Path
# Get the full path to this script.
script_path = Path(os.path.realpath(__file__))
# The parent of the script is the bin directory.
# The parent of the bin directory is the notebook directory.
# It's this notebook directory we want.
notebook_directory = script_path.parent.parent
# Add the notebook directory to our import path.
sys.path.append(str(notebook_directory))
# Add the startup directory to our import path.
startup_directory = notebook_directory / "meta" / "startup"
sys.path.append(str(startup_directory))
import argparse
import logging
import projectsetup # noqa: F401
import typing
from decimal import Decimal
from solana.account import Account
from solana.system_program import CreateAccountParams, create_account
from solana.transaction import Transaction
from spl.token.constants import ACCOUNT_LEN, TOKEN_PROGRAM_ID, WRAPPED_SOL_MINT
from spl.token.instructions import CloseAccountParams, InitializeAccountParams, Transfer2Params, close_account, initialize_account, transfer2
from BaseModel import TokenAccount, TokenLookup
from Constants import SOL_DECIMAL_DIVISOR, SOL_DECIMALS, WARNING_DISCLAIMER_TEXT
from Context import default_context as context
from Wallet import Wallet
parser = argparse.ArgumentParser(description='Unwraps Wrapped SOL to Pure SOL and adds it to the wallet account.')
parser.add_argument("--quantity", type=Decimal, required=True, help="quantity of SOL to unwrap")
parser.add_argument("--id-file", type=str, default="id.json",
help="file containing the JSON-formatted wallet private key")
parser.add_argument("--log-level", default=logging.WARNING, type=lambda level: getattr(logging, level),
help="level of verbosity to log (possible values: DEBUG, INFO, WARNING, ERROR, CRITICAL)")
parser.add_argument("--overwrite", action="store_true", default=False,
help="overwrite the ID file, if it exists (use with care!)")
args = parser.parse_args()
logging.getLogger().setLevel(args.log_level)
logging.warning(WARNING_DISCLAIMER_TEXT)
id_filename = args.id_file
if not os.path.isfile(id_filename):
logging.error(f"Wallet file '{id_filename}' is not present.")
sys.exit(1)
wallet = Wallet.load(id_filename)
lookups = TokenLookup.default_lookups()
wrapped_sol = lookups.find_by_symbol("SOL")
largest_token_account = TokenAccount.fetch_largest_for_owner_and_token(context, wallet.address, wrapped_sol)
if largest_token_account is None:
raise Exception(f"No {wrapped_sol.name} accounts found for owner {wallet.address}.")
# Overpay - remainder should be sent back to our wallet.
FEE = Decimal(".005")
lamports_to_transfer = int((args.quantity + FEE) * SOL_DECIMAL_DIVISOR)
transaction = Transaction()
signers: typing.List[Account] = [wallet.account]
wrapped_sol_account = Account()
signers.append(wrapped_sol_account)
transaction.add(
create_account(
CreateAccountParams(
from_pubkey=wallet.address,
new_account_pubkey=wrapped_sol_account.public_key(),
lamports=int(FEE * SOL_DECIMAL_DIVISOR),
space=ACCOUNT_LEN,
program_id=TOKEN_PROGRAM_ID,
)
)
)
transaction.add(
initialize_account(
InitializeAccountParams(
account=wrapped_sol_account.public_key(),
mint=WRAPPED_SOL_MINT,
owner=wallet.address,
program_id=TOKEN_PROGRAM_ID,
)
)
)
transaction.add(
transfer2(
Transfer2Params(
amount=int(args.quantity * SOL_DECIMAL_DIVISOR),
decimals=int(SOL_DECIMALS),
dest=wrapped_sol_account.public_key(),
mint=WRAPPED_SOL_MINT,
owner=wallet.address,
program_id=TOKEN_PROGRAM_ID,
source=largest_token_account.address
)
)
)
transaction.add(
close_account(
CloseAccountParams(
account=wrapped_sol_account.public_key(),
owner=wallet.address,
dest=wallet.address,
program_id=TOKEN_PROGRAM_ID,
)
)
)
print("Unwrapping SOL:")
print(f" Temporary account: {wrapped_sol_account.public_key()}")
print(f" Source: {largest_token_account.address}")
print(f" Destination: {wallet.address}")
response = context.client.send_transaction(transaction, *signers)
transaction_id = context.unwrap_transaction_id_or_raise_exception(response)
print(f"Waiting on transaction ID: {transaction_id}")
context.wait_for_confirmation(transaction_id)
print("Transaction confirmed.")

138
bin/wrap-sol Executable file
View File

@ -0,0 +1,138 @@
#!/usr/bin/env pyston3
import os
import sys
from pathlib import Path
# Get the full path to this script.
script_path = Path(os.path.realpath(__file__))
# The parent of the script is the bin directory.
# The parent of the bin directory is the notebook directory.
# It's this notebook directory we want.
notebook_directory = script_path.parent.parent
# Add the notebook directory to our import path.
sys.path.append(str(notebook_directory))
# Add the startup directory to our import path.
startup_directory = notebook_directory / "meta" / "startup"
sys.path.append(str(startup_directory))
import argparse
import logging
import projectsetup # noqa: F401
import typing
from decimal import Decimal
from solana.account import Account
from solana.system_program import CreateAccountParams, create_account
from solana.transaction import Transaction
from spl.token.constants import ACCOUNT_LEN, TOKEN_PROGRAM_ID, WRAPPED_SOL_MINT
from spl.token.instructions import CloseAccountParams, InitializeAccountParams, Transfer2Params, close_account, initialize_account, transfer2
from BaseModel import TokenAccount, TokenLookup
from Constants import SOL_DECIMAL_DIVISOR, SOL_DECIMALS, WARNING_DISCLAIMER_TEXT
from Context import default_context as context
from Wallet import Wallet
parser = argparse.ArgumentParser(description='Creates a new wallet and private key, and saves it to a file.')
parser.add_argument("--quantity", type=Decimal, required=True, help="quantity of SOL to wrap")
parser.add_argument("--id-file", type=str, default="id.json",
help="file containing the JSON-formatted wallet private key")
parser.add_argument("--log-level", default=logging.WARNING, type=lambda level: getattr(logging, level),
help="level of verbosity to log (possible values: DEBUG, INFO, WARNING, ERROR, CRITICAL)")
parser.add_argument("--overwrite", action="store_true", default=False,
help="overwrite the ID file, if it exists (use with care!)")
args = parser.parse_args()
logging.getLogger().setLevel(args.log_level)
logging.warning(WARNING_DISCLAIMER_TEXT)
id_filename = args.id_file
if not os.path.isfile(id_filename):
logging.error(f"Wallet file '{id_filename}' is not present.")
sys.exit(1)
wallet = Wallet.load(id_filename)
lookups = TokenLookup.default_lookups()
wrapped_sol = lookups.find_by_symbol("SOL")
token_accounts = TokenAccount.fetch_all_for_owner_and_token(context, wallet.address, wrapped_sol)
if len(token_accounts) == 0:
close_wrapping_account = False
else:
close_wrapping_account = True
# Overpay - remainder should be sent back to our wallet.
FEE = Decimal(".005")
lamports_to_transfer = int((args.quantity + FEE) * SOL_DECIMAL_DIVISOR)
transaction = Transaction()
signers: typing.List[Account] = [wallet.account]
wrapped_sol_account = Account()
signers.append(wrapped_sol_account)
transaction.add(
create_account(
CreateAccountParams(
from_pubkey=wallet.address,
new_account_pubkey=wrapped_sol_account.public_key(),
lamports=lamports_to_transfer,
space=ACCOUNT_LEN,
program_id=TOKEN_PROGRAM_ID,
)
)
)
transaction.add(
initialize_account(
InitializeAccountParams(
account=wrapped_sol_account.public_key(),
mint=WRAPPED_SOL_MINT,
owner=wallet.address,
program_id=TOKEN_PROGRAM_ID,
)
)
)
print("Wrapping SOL:")
if len(token_accounts) == 0:
print(f" Source: {wallet.address}")
print(f" Destination: {wrapped_sol_account.public_key()}")
else:
print(f" Temporary account: {wrapped_sol_account.public_key()}")
print(f" Source: {wallet.address}")
print(f" Destination: {token_accounts[0].address}")
transaction.add(
transfer2(
Transfer2Params(
amount=int(args.quantity * SOL_DECIMAL_DIVISOR),
decimals=int(SOL_DECIMALS),
dest=token_accounts[0].address,
mint=WRAPPED_SOL_MINT,
owner=wallet.address,
program_id=TOKEN_PROGRAM_ID,
source=wrapped_sol_account.public_key()
)
)
)
transaction.add(
close_account(
CloseAccountParams(
account=wrapped_sol_account.public_key(),
owner=wallet.address,
dest=wallet.address,
program_id=TOKEN_PROGRAM_ID,
)
)
)
response = context.client.send_transaction(transaction, *signers)
transaction_id = context.unwrap_transaction_id_or_raise_exception(response)
print(f"Waiting on transaction ID: {transaction_id}")
context.wait_for_confirmation(transaction_id)
print("Done.")