Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add transaction compression mode bulkTransfer #586

Merged
merged 3 commits into from
Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
38 changes: 38 additions & 0 deletions app/model/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""
Copyright BOOSTRY Co., Ltd.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

See the License for the specific language governing permissions and
limitations under the License.

SPDX-License-Identifier: Apache-2.0
"""
from typing import Annotated, Any

from pydantic import WrapValidator
from pydantic_core.core_schema import ValidatorFunctionWrapHandler
from web3 import Web3


def ethereum_address_validator(
value: Any, handler: ValidatorFunctionWrapHandler, *args, **kwargs
):
"""Validator for ethereum address"""
if value is not None:
if not isinstance(value, str):
raise ValueError(f"value must be of string")
if not Web3.is_address(value):
raise ValueError("invalid ethereum address")
return value


EthereumAddress = Annotated[str, WrapValidator(ethereum_address_validator)]
Copy link
Member Author

@YoshihitoAso YoshihitoAso Jan 14, 2024

Choose a reason for hiding this comment

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

I also improved Address validation.

31 changes: 31 additions & 0 deletions app/model/blockchain/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from app.model.blockchain.tx_params.ibet_security_token import (
AdditionalIssueParams as IbetSecurityTokenAdditionalIssueParams,
ApproveTransferParams as IbetSecurityTokenApproveTransfer,
BulkTransferParams as IbetSecurityTokenBulkTransferParams,
CancelTransferParams as IbetSecurityTokenCancelTransfer,
ForceUnlockParams as IbetSecurityTokenForceUnlockParams,
LockParams as IbetSecurityTokenLockParams,
Expand Down Expand Up @@ -191,6 +192,36 @@ def transfer(

return tx_hash

def bulk_transfer(
self, data: IbetSecurityTokenBulkTransferParams, tx_from: str, private_key: str
):
"""Transfer ownership"""
try:
contract = ContractUtils.get_contract(
contract_name=self.contract_name, contract_address=self.token_address
)
tx = contract.functions.bulkTransfer(
data.to_address_list, data.amount_list
).build_transaction(
{
"chainId": CHAIN_ID,
"from": tx_from,
"gas": TX_GAS_LIMIT,
"gasPrice": 0,
}
)
tx_hash, _ = ContractUtils.send_transaction(
transaction=tx, private_key=private_key
)
except ContractRevertError:
raise
except TimeExhausted as timeout_error:
raise SendTransactionError(timeout_error)
except Exception as err:
raise SendTransactionError(err)

return tx_hash

def additional_issue(
self,
data: IbetSecurityTokenAdditionalIssueParams,
Expand Down
78 changes: 14 additions & 64 deletions app/model/blockchain/tx_params/ibet_security_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,53 +16,31 @@

SPDX-License-Identifier: Apache-2.0
"""
from pydantic import BaseModel, PositiveInt, field_validator
from web3 import Web3
from pydantic import BaseModel, PositiveInt

from app.model import EthereumAddress


class TransferParams(BaseModel):
from_address: str
to_address: str
from_address: EthereumAddress
to_address: EthereumAddress
amount: PositiveInt

@field_validator("from_address")
@classmethod
def from_address_is_valid_address(cls, v):
if not Web3.is_address(v):
raise ValueError("from_address is not a valid address")
return v

@field_validator("to_address")
@classmethod
def to_address_is_valid_address(cls, v):
if not Web3.is_address(v):
raise ValueError("to_address is not a valid address")
return v
class BulkTransferParams(BaseModel):
to_address_list: list[EthereumAddress]
amount_list: list[PositiveInt]


class AdditionalIssueParams(BaseModel):
account_address: str
account_address: EthereumAddress
amount: PositiveInt

@field_validator("account_address")
@classmethod
def account_address_is_valid_address(cls, v):
if not Web3.is_address(v):
raise ValueError("account_address is not a valid address")
return v


class RedeemParams(BaseModel):
account_address: str
account_address: EthereumAddress
amount: PositiveInt

@field_validator("account_address")
@classmethod
def account_address_is_valid_address(cls, v):
if not Web3.is_address(v):
raise ValueError("account_address is not a valid address")
return v


class ApproveTransferParams(BaseModel):
application_id: int
Expand All @@ -75,42 +53,14 @@ class CancelTransferParams(BaseModel):


class LockParams(BaseModel):
lock_address: str
lock_address: EthereumAddress
value: PositiveInt
data: str

@field_validator("lock_address")
@classmethod
def lock_address_is_valid_address(cls, v):
if not Web3.is_address(v):
raise ValueError("lock_address is not a valid address")
return v


class ForceUnlockParams(BaseModel):
lock_address: str
account_address: str
recipient_address: str
lock_address: EthereumAddress
account_address: EthereumAddress
recipient_address: EthereumAddress
value: PositiveInt
data: str

@field_validator("lock_address")
@classmethod
def lock_address_is_valid_address(cls, v):
if not Web3.is_address(v):
raise ValueError("lock_address is not a valid address")
return v

@field_validator("account_address")
@classmethod
def account_address_is_valid_address(cls, v):
if not Web3.is_address(v):
raise ValueError("account_address is not a valid address")
return v

@field_validator("recipient_address")
@classmethod
def recipient_address_is_valid_address(cls, v):
if not Web3.is_address(v):
raise ValueError("recipient_address is not a valid address")
return v
26 changes: 8 additions & 18 deletions app/model/blockchain/tx_params/ibet_share.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@
from typing import Optional

from pydantic import BaseModel, field_validator
from web3 import Web3

from app.model import EthereumAddress

from .ibet_security_token import (
AdditionalIssueParams as IbetSecurityTokenAdditionalIssueParams,
ApproveTransferParams as IbetSecurityTokenApproveTransferParams,
BulkTransferParams as IbetSecurityTokenBulkTransferParams,
CancelTransferParams as IbetSecurityTokenCancelTransferParams,
ForceUnlockParams as IbetSecurityTokenForceUnlockParams,
LockParams as IbetSecurityTokenLockParams,
Expand All @@ -39,8 +41,8 @@ class UpdateParams(BaseModel):
dividend_record_date: Optional[str] = None
dividend_payment_date: Optional[str] = None
dividends: Optional[float] = None
tradable_exchange_contract_address: Optional[str] = None
personal_info_contract_address: Optional[str] = None
tradable_exchange_contract_address: Optional[EthereumAddress] = None
personal_info_contract_address: Optional[EthereumAddress] = None
transferable: Optional[bool] = None
status: Optional[bool] = None
is_offering: Optional[bool] = None
Expand All @@ -61,24 +63,12 @@ def dividends_13_decimal_places(cls, v):
raise ValueError("dividends must be rounded to 13 decimal places")
return v

@field_validator("tradable_exchange_contract_address")
@classmethod
def tradable_exchange_contract_address_is_valid_address(cls, v):
if v is not None and not Web3.is_address(v):
raise ValueError(
"tradable_exchange_contract_address is not a valid address"
)
return v

@field_validator("personal_info_contract_address")
@classmethod
def personal_info_contract_address_is_valid_address(cls, v):
if v is not None and not Web3.is_address(v):
raise ValueError("personal_info_contract_address is not a valid address")
return v
class TransferParams(IbetSecurityTokenTransferParams):
pass


class TransferParams(IbetSecurityTokenTransferParams):
class BulkTransferParams(IbetSecurityTokenBulkTransferParams):
pass


Expand Down
26 changes: 8 additions & 18 deletions app/model/blockchain/tx_params/ibet_straight_bond.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@
from typing import List, Optional

from pydantic import BaseModel, field_validator
from web3 import Web3

from app.model import EthereumAddress

from .ibet_security_token import (
AdditionalIssueParams as IbetSecurityTokenAdditionalIssueParams,
ApproveTransferParams as IbetSecurityTokenApproveTransferParams,
BulkTransferParams as IbetSecurityTokenBulkTransferParams,
CancelTransferParams as IbetSecurityTokenCancelTransferParams,
ForceUnlockParams as IbetSecurityTokenForceUnlockParams,
LockParams as IbetSecurityTokenLockParams,
Expand All @@ -47,8 +49,8 @@ class UpdateParams(BaseModel):
status: Optional[bool] = None
is_offering: Optional[bool] = None
is_redeemed: Optional[bool] = None
tradable_exchange_contract_address: Optional[str] = None
personal_info_contract_address: Optional[str] = None
tradable_exchange_contract_address: Optional[EthereumAddress] = None
personal_info_contract_address: Optional[EthereumAddress] = None
contact_information: Optional[str] = None
privacy_policy: Optional[str] = None
transfer_approval_required: Optional[bool] = None
Expand Down Expand Up @@ -83,24 +85,12 @@ def interest_payment_date_list_length_less_than_13(cls, v):
)
return v

@field_validator("tradable_exchange_contract_address")
@classmethod
def tradable_exchange_contract_address_is_valid_address(cls, v):
if v is not None and not Web3.is_address(v):
raise ValueError(
"tradable_exchange_contract_address is not a valid address"
)
return v

@field_validator("personal_info_contract_address")
@classmethod
def personal_info_contract_address_is_valid_address(cls, v):
if v is not None and not Web3.is_address(v):
raise ValueError("personal_info_contract_address is not a valid address")
return v
class TransferParams(IbetSecurityTokenTransferParams):
pass


class TransferParams(IbetSecurityTokenTransferParams):
class BulkTransferParams(IbetSecurityTokenBulkTransferParams):
pass


Expand Down
6 changes: 4 additions & 2 deletions app/model/db/bulk_transfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

SPDX-License-Identifier: Apache-2.0
"""
from sqlalchemy import BigInteger, Integer, String
from sqlalchemy import BigInteger, Boolean, Integer, String
from sqlalchemy.orm import Mapped, mapped_column

from .base import Base
Expand All @@ -33,6 +33,8 @@ class BulkTransferUpload(Base):
issuer_address: Mapped[str] = mapped_column(String(42), nullable=False, index=True)
# token type
token_type: Mapped[str] = mapped_column(String(40), nullable=False)
# transaction compression
transaction_compression: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
# processing status (pending:0, succeeded:1, failed:2)
status: Mapped[int] = mapped_column(Integer, nullable=False, index=True)

Expand All @@ -43,7 +45,7 @@ class BulkTransfer(Base):
__tablename__ = "bulk_transfer"

# sequence id
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
# issuer address
issuer_address: Mapped[str] = mapped_column(String(42), nullable=False, index=True)
# upload id (UUID)
Expand Down
2 changes: 2 additions & 0 deletions app/model/schema/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
BulkTransferResponse,
BulkTransferUploadIdResponse,
BulkTransferUploadResponse,
IbetShareBulkTransferRequest,
IbetStraightBondBulkTransferRequest,
)
from .e2e_messaging import (
E2EMessagingAccountChangeEOAPasswordRequest,
Expand Down
1 change: 1 addition & 0 deletions app/model/schema/base/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@
MMDD_constr,
ResultSet,
SortOrder,
TokenType,
YYYYMMDD_constr,
)
7 changes: 6 additions & 1 deletion app/model/schema/base/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

SPDX-License-Identifier: Apache-2.0
"""
from enum import IntEnum, StrEnum
from enum import Enum, IntEnum, StrEnum
from typing import Literal, Optional

from pydantic import BaseModel, Field, StringConstraints
Expand Down Expand Up @@ -48,6 +48,11 @@ class IbetShareContractVersion(StrEnum):
EMPTY_str = Literal[""]


class TokenType(str, Enum):
IBET_STRAIGHT_BOND = "IbetStraightBond"
IBET_SHARE = "IbetShare"


############################
# REQUEST
############################
Expand Down
4 changes: 1 addition & 3 deletions app/model/schema/batch_issue_redeem.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@

from pydantic import BaseModel, ConfigDict, Field

from app.model.db import TokenType
from app.model.schema.base import ResultSet

from .base import ResultSet, TokenType
from .personal_info import PersonalInfo

############################
Expand Down
Loading
Loading