Skip to content

Commit

Permalink
updated styling and added suggestions from endogen
Browse files Browse the repository at this point in the history
  • Loading branch information
duelingbenjos committed Jun 14, 2024
1 parent c6f5de1 commit 4f6781f
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 90 deletions.
5 changes: 2 additions & 3 deletions .vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ export default defineConfig({
text: 'Smart Contracts',
collapsed: false,
items: [
{ text: 'Contracting Library', link: '/smart-contracts/' },
{ text: 'Contracting Cheat Sheet', link: '/smart-contracts/contract-cheat-sheet' },
{ text: 'Contracting Engine', link: '/smart-contracts/' },
{ text: 'Cheat Sheet', link: '/smart-contracts/contract-cheat-sheet' },
{ text: 'Context', link: '/smart-contracts/context' },
{ text: 'Functions', link: '/smart-contracts/functions' },
{
Expand Down Expand Up @@ -74,5 +74,4 @@ export default defineConfig({
cleanUrls: true,
srcDir: './src',
base: '/',

})
13 changes: 13 additions & 0 deletions .vitepress/theme/custom.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
pre code {
font-size: 0.75rem !important;
font-family: monospace;
}

p {
font-size: 0.84rem;
}

.info, .tip, .warning {
font-size: 0.8rem;
}

4 changes: 4 additions & 0 deletions .vitepress/theme/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import DefaultTheme from 'vitepress/theme'
import './custom.css'

export default DefaultTheme
31 changes: 13 additions & 18 deletions src/introduction/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,25 @@

## Introduction to Xian

Xian is a pioneering blockchain project that melds innovation with accessibility in decentralized technology. Combining the speed and robustness of CometBFT consensus with the simplicity and elegance of Python, Xian offers a secure, scalable, and developer-friendly platform. The project shines with its use of Contracting, a subset of Python designed for writing blockchain applications, which makes smart contract development extremely accessible.

## The Vision of Xian

Our vision is to democratize blockchain development, creating an inclusive ecosystem where developers of all backgrounds can innovate. By leveraging Python, we tap into one of the largest developer communities, fostering creativity and diversity.
Xian is a pioneering blockchain project that melds innovation with accessibility in decentralized technology. Combining the speed and robustness of CometBFT consensus with the simplicity and elegance of Python, Xian offers a secure, scalable, and developer-friendly platform.

## Why Xian?

- **Developer Accessibility:** Contracting simplifies smart contract development, welcoming developers from varied backgrounds without sacrificing functionality or security.
- **Innovative Blockchain Solutions:** Xian offers a familiar environment for developers, where they can feel safe to push the boundaries of dApp development, using many of the tools available from the expansive python ecosystem.
- **Empowered Governance:** Our DAO-based governance model ensures community-driven, transparent decision-making, giving stakeholders a voice in the project's direction.

## Get involved

Whether you're a developer, tech enthusiast, or haven't yet begun your development journey, Xian will offer a rich, welcoming experience. Dive into our vibrant ecosystem and start playing around and building things today.
Powered by Python's simplicity and community-driven innovation, let's explore the endless possibilities together !
- **Developer Accessibility:** Python is the biggest developer community in the world and it’s still growing every year. But it’s weird because there is no blockchain that allows for native python smart contracts. That’s exactly what we introduce.
- **Accessibility and Speed:** Battle-tested consensus engine written in Go to be fast and Smart contract engine written in Python to allow for native Python contracts that are extremely simple / easy to write.
- **Community Driven Governance:** Our DAO-based governance model ensures community-driven, transparent decision-making, giving stakeholders a voice in the project's direction.

- Are we missing some features or tools you'd love to see ? Let us know, we might make a bounty for it.
- Found a bug ? Lets squash it.
- Got a cool idea for something to build ? We'd love to hear about it and support you to explore it more.
- Find a vulnerability ? Let us know and we'll reward you in accordance with it's severity.

:::tip Key Concepts
- **Low transaction fees**
- **Instant finality**
- **Smart Contracts in native Python**
- **2 second blocktime**
- **Consensus layer written in Go, Smart Contract engine in Python**
- **Developers earn through contract usage**
:::

## Come and talk to us !
## Get involved

Telegram : https://t.me/xian_network <br/>
Discord : https://discord.gg/8W9ZMxUYuA <br/>
Expand Down
114 changes: 73 additions & 41 deletions src/smart-contracts/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,38 +21,47 @@ There are six types of `ctx` variables.
| `ctx.entry` | The entry function and contract as a tuple. | ctx.entry can help you distinguish a caller (either user or contract) and if the caller is a contract, it will inform you about the method from which that contract called your contract. |
| `ctx.submission_name` | The name of the submission contract, usually 'submission'. | |


:::tip A note on account & contract addresses
- In Contracting, account addresses are 32-byte hexadecimal strings.
- For these examples, accounts are represented as shortened versions, e.g. `db21a73137672f075f9a8ee142a1aa4839a5deb28ef03a10f3e7e16c87db8f24` will be represented as `db21a731`.
- Contract addresses, with the exception of system contracts always start with `con_`, e.g. `con_direct`.
:::

## ctx.caller

This is the most complex Context variable, but also the most useful. The ctx.caller is the same as the transaction signer (ctx.signer) at the beginning of execution. If the smart contract that is initially invoked calls a function on another smart contract, the ctx.caller then changes to the name of the smart contract calling that function, and so on and so forth until the end of the execution.
This is the most complex Context variable, but also the most useful. The `ctx.caller` is the same as the transaction signer (`ctx.signer`) at the beginning of execution. If the smart contract that is initially invoked calls a function on another smart contract, the `ctx.caller` then changes to the name of the smart contract calling that function, and so on and so forth until the end of the execution.

direct.py (Smart Contract)
:::tip `con_direct` **smart-contract**
```python
@export
def who_am_i():
return ctx.caller
```

indirect.py (Smart Contract)
`con_indirect` **smart-contract**
```python
import direct
import con_direct

@export
def call_direct():
return direct.who_am_i()
return con_direct.who_am_i()
```
:::

Assume the two contracts above exist in state space. If `stu` calls `who_am_i` on the `direct` contract, `stu` will be returned because `direct` does not call any functions in any other smart contracts.

However, if `stu` calls `call_direct` on the `indirect` contract, `indirect` will be returned because `indirect` is now the caller of this function.
:::info Assume the two contracts above exist in state space.
- If `2fadab39` calls `who_am_i` on the `con_direct` contract, `2fadab39` will be returned because `con_direct` does not call any functions in any other smart contracts.
- However, if `2fadab39` calls `call_direct` on the `con_indirect` contract, `con_indirect` will be returned because `con_indirect` is now the caller of this function.

:::
A good example of how to use this would be in a token contract.

token.py (Smart Contract)
:::tip `con_token` smart-contract
```python
balances = Hash()
@construct
def seed():
balances['stu'] = 100
balances['2fadab39'] = 100
balances['contract'] = 99

@export
Expand All @@ -63,26 +72,29 @@ def send(amount, to):
balances[to] += amount
```

contract.py (Smart Contract)
`con_contract` **smart-contract**
```python
import token
import con_token

@export
def withdraw(amount):
assert ctx.caller == 'stu'
assert ctx.caller == '2fadab39'

token.send(amount, ctx.caller)
con_token.send(amount, ctx.caller)
```

In the above setup, `stu` has 100 tokens directly on the `token` contract. He can send them, because his account balance is looked up based on the `ctx.caller` when the send function is called.

Similarly, `contract` also has 99 tokens. When `contract` imports `token` and calls `send`, `ctx.caller` is changed to `contract`, and its balance is looked up and mutated accordingly.

:::

:::info In the above example:
- `2fadab39` has 100 tokens directly on the `token` contract.
- She can send them, because his account balance is looked up based on the `ctx.caller` when the send function is called.
- `con_contract` also has 99 tokens.
- When `con_contract` imports `con_token` and calls `send`, `ctx.caller` is changed to `con_contract`, and its balance is looked up and changed accordingly.
:::
### ctx.this

This is a very simple reference to the name of the smart contract. Use cases are generally when you need to identify a smart contract itself when doing some sort of transaction, such as sending payment through an account managed by the smart contract but residing in another smart contract.

registrar.py (Smart Contract)
:::tip `con_registrar` **smart-contract**
```python
names = Hash()

Expand All @@ -92,73 +104,93 @@ def register(name, value):
names[name] = value
```

controller.py (Smart Contract)
`con_controller` **smart-contract**
```python
import registrar

@export
def register(value):
registrar.register(ctx.this, value)
def register():
registrar.register(ctx.this, "some_value")
```
:::

:::info In the above example:
The arguments passed to `register` on `con_registrar` will be `con_controller` and `some_value`.
:::

## ctx.signer

This is the absolute signer of the transaction regardless of where the code is being executed in the call stack. This is good for creating blacklists of users from a particular contract.

blacklist.py (Smart Contract)
:::tip `con_blacklist` **smart-contract**
```python
not_allowed = ['stu', 'tejas']
not_allowed = ['2fadab39', 'db21a731']

@export
def some_func():
assert ctx.signer not in not_allowed
return 'You are not blacklisted!'
```

indirect.py (Smart Contract)
`con_indirect` **smart-contract**
```python
import blacklist
import con_blacklist

@export
def try_to_bypass():
return blacklist.some_func()
return con_blacklist.some_func()
```

In the case that `stu` calls the `try_to_bypass` function on `indirect`, the transaction will still fail because `ctx.signer` is used for gating instead of `ctx.caller`.

__NOTE__: Never use `ctx.signer` for account creation or identity. Only use it for security guarding and protection. `ctx.caller` should allow behavior based on the value. `ctx.signer` should block behavior based on the value.

:::
:::info In the above example:
`2fadab39` calls the `try_to_bypass` function on `con_indirect.

The transaction will still fail because `ctx.signer` is used for gating instead of `ctx.caller`.
:::
:::warning NOTE
Never use `ctx.signer` for account creation or identity. Only use it for security guarding and protection. `ctx.caller` should allow behavior based on the value. `ctx.signer` should block behavior based on the value.
:::
### ctx.owner

On submission, you can specify the owner of a smart contract. This means that only the owner can call the `@export` functions on it. This is for advanced contract pattern types where a single controller is desired for many 'sub-contracts'. Using `ctx.owner` inside of a smart contract can only be used to change the ownership of the contract itself. Be careful with this method!

ownable.py (Smart Contract)
:::tip `con_ownable` **smart-contract**
```python
@export
def change_ownership(new_owner):
ctx.owner = new_owner
```
:::


The above contract is not callable unless the `ctx.caller` is the `ctx.owner`. Therefore, you do not need to do additional checks to make sure that this is the case.
:::info In the above example:
The contract is not callable unless the `ctx.caller` is the `ctx.owner`.
Therefore, you do not need to do additional checks to make sure that this is the case.
:::

## ctx.entry

When someone calls a contract through another contract, you might want to know what contract and function it was that called it in the first place.

`ctx.entry` returns a tuple containing the name of the contract and the function that was called.

contract.py (Smart Contract)
:::tip `con_contract` **smart-contract**
```python
@export
def function():
return ctx.entry # Output when someone used other_contract: ("other_contract","call_contract")
# Output when someone used con_other_contract: ("con_other_contract","call_contract")
return ctx.entry
```

other_contract.py (Smart Contract)
`con_other_contract` **smart-contract**
```python
import contract
import con_contract

@export
def call_contract():
contract.function()
```
con_contract.function()
```
:::

:::info In the above example :
The output of `con_contract.function()` will be `("con_other_contract", "call_contract")`.

34 changes: 19 additions & 15 deletions src/smart-contracts/contract-cheat-sheet.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
title: Contracting Cheat Sheet
title: Cheat Sheet
description: Here is a reference contract showcasing many of the syntax and features of Contracting.
---

# Contracting Cheat Sheet
# Cheat Sheet

## Reference Contract

Expand All @@ -24,9 +24,14 @@ submission_block_hash = Variable()
random_number = Variable()

# ForeignHash is a way to get a read-only view of a hash from another contract
currency_balances = ForeignHash(foreign_contract='currency', foreign_name='balances')
# ForeignVariable is a way to get a read-only view of a variable from another contract
foundation_owner = ForeignVariable(foreign_contract='foundation', foreign_name='owner')
currency_balances = ForeignHash(
foreign_contract='currency', foreign_name='balances'
)
# ForeignVariable is a way to get a read-only view of a variable from another
# contract
foundation_owner = ForeignVariable(
foreign_contract='foundation', foreign_name='owner'
)

# The construct decorator is used to define initialization logic for the contract
@construct
Expand All @@ -43,11 +48,10 @@ def seed():

# This function is private and cannot be called from outside the contract
def private_function():


return "This is a private function"

# The export decorator is used to define functions that can be called from outside the contract
# The export decorator is used to define functions that can be called from
# outside the contract
@export
def call_private_function():
# Call the private function
Expand Down Expand Up @@ -81,13 +85,11 @@ def get_storage_pair(key: str):
# Set a nested key-value pair in the storage
@export
def set_nested_storage_pair(key: str, nested_key: str, value: int):

storage[key, nested_key] = value

@export
# Get the value of a nested key in the storage
def get_nested_storage_pair(key: str, nested_key: str):

return storage[key, nested_key]

# Import another contract dynamically
Expand All @@ -96,9 +98,11 @@ def interact_with_other_contract(contract: str, args: dict):
c = importlib.import_module(contract)

forced_interface = [
# Func is a way to enforce the existence of a function with specific arguments
# Func is a way to enforce the existence of a function with specific
# arguments
importlib.Func('do_something', args=('amount', 'to')),
# Var is a way to enforce the existence of a variable with a specific type
# Var is a way to enforce the existence of a variable with a specific
# type
importlib.Var('balances', Hash)
]

Expand All @@ -111,7 +115,6 @@ def interact_with_other_contract(contract: str, args: dict):
# Check if the submission time is older than a specific date
@export
def is_older_than_date(date: datetime.datetime):

return submission_time.get() < date

@export
Expand All @@ -123,8 +126,9 @@ def get_contract_name():
def who_am_i():
return ctx.caller

# First signer in the call chain (the original signer). This is the account that initiated the transaction even if the transaction was
# forwarded by another contract
# First signer in the call chain (the original signer). This is the account that
# initiated the transaction even if the transaction was forwarded by another
# contract
@export
def get_top_level_signer():
return ctx.signer
Expand Down
Loading

0 comments on commit 4f6781f

Please sign in to comment.